savon 2.0.3 → 2.1.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.
- data/.gitignore +1 -0
- data/CHANGELOG.md +38 -6
- data/Gemfile +1 -0
- data/lib/savon.rb +3 -3
- data/lib/savon/builder.rb +14 -3
- data/lib/savon/client.rb +22 -9
- data/lib/savon/operation.rb +70 -2
- data/lib/savon/options.rb +26 -20
- data/lib/savon/qualified_message.rb +2 -6
- data/lib/savon/request.rb +52 -79
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +11 -2
- data/savon.sublime-workspace +494 -0
- data/spec/fixtures/ssl/client_encrypted_key.pem +30 -0
- data/spec/fixtures/ssl/client_encrypted_key_cert.pem +24 -0
- data/spec/integration/support/application.rb +10 -8
- data/spec/savon/client_spec.rb +27 -18
- data/spec/savon/operation_spec.rb +112 -7
- data/spec/savon/options_spec.rb +126 -28
- data/spec/savon/request_spec.rb +404 -84
- data/spec/spec_helper.rb +11 -0
- metadata +25 -6
@@ -0,0 +1,30 @@
|
|
1
|
+
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
2
|
+
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIi+Fi+FH7OtACAggA
|
3
|
+
MBQGCCqGSIb3DQMHBAivzaRTYRNk4QSCBMhnQ7/NN0ljkAwADHknQi4dgbBU+FV+
|
4
|
+
vY+ypRCTLp7UongPMDS+pC6TyOFGeHz4Yri3UvmEIN5DlBlAfjPI6+lBswjOIrpw
|
5
|
+
6CaZXrX4oefjh73c2OcQlYKw9w03ppfmfO0v1t6oPLiK6M8sNQ5lgb8d9eG3r6Dp
|
6
|
+
LqIp4I6WeGcoXIpVYE35sz6wmLQ2Q626KY/5BPVAgMVG3K1g1haxZIAQBQE63cqz
|
7
|
+
JK3IUiG2r6Q6vOyZ+Iz9KolEf3RVvW/RgOrb0dLbbLkDOL8G6dXDgWEeYtqGZpPR
|
8
|
+
BktU2Kf7lr0BAgbI3eLubmIufhonoV4VkHVYu1ZSACwSl2HEqDl5aF5hP3wOtfS5
|
9
|
+
Ls62Z1ATO/24dG1oI8xL3YCeTzoa1Lmyeh+HFRncoVU5CdQgyzY9d5yr1x70AwN+
|
10
|
+
MpVwd0+WGyESiRVd4dN8n99SY/bTaYJxv8P+wOrbjld9Q3mF3vxx6Nkkfboai1wD
|
11
|
+
bY9i/B5/TZip5FBnZbJiYakc+yoB6Bf1UuIZA9T9EIY2K7VhTeuEjTTqJVf7dp/C
|
12
|
+
ZqVSNCHO3eAUMByrshznw2YCia8Q1VAXgIbnZ8RvUxxIZVUDTxuWPkBJkcrmMgKt
|
13
|
+
GvD2YYIOIuFwTLCFBTlcXNl8kNYc9VRAnK7efi9xrzINod0VSV5hj1PYT4e2khnS
|
14
|
+
4cngMTbbNwWP8Rg7pSxzwWIwc8Zkytde5gnfkBFv+g8o+JRM2ZB3wkiUhEkf0Vht
|
15
|
+
gl3K9LFqdqN+EsRjXR/a16sVK3Uer7zcy/NLzvo/rF0YKRmb+apDIFO/vtCX9qyH
|
16
|
+
+pBofVO+RNb2T2ZY1iSvyjv/d7nNXRnLArecralQjekh+AKIBsl5R0nsSnQu6ydn
|
17
|
+
yDteKDuOPnVl4qQowVbmGg7juHW9j4u98H7cW4RN/txegG1J7gbFFdl2bjYQ/PGZ
|
18
|
+
iAG53QvjmvRRaiPCNOB3PYm1yO/1vCPAKlOeBYywF53BSxCDx9OjP0eXROdQGiZV
|
19
|
+
XEkDqf742R9/8Fy1ETcriEzRVWv4nSRmB+yfMfHcTZZiJnEF9RUYQVxBVfpwBi8t
|
20
|
+
I8N46L2iNeY4itbN8Ke3U3EfntdoZMNI1uN/haDmLFuRuzZIBkSl3YsnsDJXaT5P
|
21
|
+
KjPPiZWkxsC3KUYeisefjNHonkM6JXqAy9ElyWrjSPhioMLTy3Qwus8NPGLWkMfI
|
22
|
+
bpy7Z63xRf9tifXoANLOqC/VVXOCn4m/eEUAMi0EQZ7QbysopFigiri/MFidXf90
|
23
|
+
aIUMiPmCTzLDBJfHozyalMVf3aJFbDJdlAmFtasAP1aPgqReAb9s+6U3wkE8VtGB
|
24
|
+
rneBPhejj9FzrWMludgPLDRvfzr6/0nG2GC7XbOnKPd0RbKFtZaJuT18Jhil5Bwb
|
25
|
+
S2MBrUnxMmOge9N5ZIyzXk+lPFRMkHmY64h+P/Op9w8tVYCFlj4HjAboVpJAGIq4
|
26
|
+
gPgT3Q/0ghRyyBjBrmSDuk+/1s7qc/lWUONFAJaSCRdhpM9I8il5QYHPM6Z9JCg+
|
27
|
+
PrfHftP+bU0xht7mVI5ew1OjWTZEym0/ALEyqddLz0qFYejKSemx8EMze8cN2wEE
|
28
|
+
OTAqyVBLo+7HJ2FbIXMgRNSc4P77jmeEG0/4WsreMOeI1/6nOiwqvipuLY9ojc9v
|
29
|
+
TGQ=
|
30
|
+
-----END ENCRYPTED PRIVATE KEY-----
|
@@ -0,0 +1,24 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEBTCCAu2gAwIBAgIJAKoJLHwFIyVbMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYD
|
3
|
+
VQQGEwJVUzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWEx
|
4
|
+
GDAWBgNVBAoMD1Rlc3RpbmcgQ28sIExMQzEQMA4GA1UECwwHVGVzdGluZzEUMBIG
|
5
|
+
A1UEAwwLZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5j
|
6
|
+
b20wHhcNMTMwMTI1MTUwNDEwWhcNNDMwMTE4MTUwNDEwWjCBmDELMAkGA1UEBhMC
|
7
|
+
VVMxETAPBgNVBAgMCFZpcmdpbmlhMRMwEQYDVQQHDApBbGV4YW5kcmlhMRgwFgYD
|
8
|
+
VQQKDA9UZXN0aW5nIENvLCBMTEMxEDAOBgNVBAsMB1Rlc3RpbmcxFDASBgNVBAMM
|
9
|
+
C2V4YW1wbGUuY29tMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIIB
|
10
|
+
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4gYiV5ufeedy/paVEJ9eJAZo
|
11
|
+
t3zFO+9UJzrIFWGhDdTB8AwX6ZE3koKV105i3nRKN+I6dS6+7APB607CqTqdzuqa
|
12
|
+
/5qLnV2d73BuTAZabeClE4/8HXsDoQH42KcPQfVQ/nCuTPjuGUEUqIWVFpwKcZmV
|
13
|
+
UP82Ezf31fOqtzVI6cSgOwWSflgFFdKRyCk0R1eNznRj2rvfYtypIplV715d1Qqk
|
14
|
+
k+XXmMIWIobNT/utgf6n39e89VPzu9EZ5vSpCflWvCkeAnjavWLD/MJktRJqQqlx
|
15
|
+
dFTWvzIxYAhJR6M+zB4a6UGDzj6NG1ujrtIdBaK1Jq6iXO4WDryRMs8RPF0UmwID
|
16
|
+
AQABo1AwTjAdBgNVHQ4EFgQUjEykvRbAg/ju1UIvMuozlXzKRpYwHwYDVR0jBBgw
|
17
|
+
FoAUjEykvRbAg/ju1UIvMuozlXzKRpYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
|
18
|
+
AQUFAAOCAQEAhvgvCfZcQlEJwRZMYd1AUOS39fs90Nc5hezaIN1I6bqzW0+ka45Y
|
19
|
+
MAxcq7BTOZfVO+DTEAB/IW7yR4Np8mzWSp7gDnyP3wyOX4Lce2lUZQ2fQI71pupf
|
20
|
+
tXwpp0bGGgzeUeJK8MFBUiFMUBiJuUMsY2hYIqJ2gYlazMEQp67jV0fdgcgj3nak
|
21
|
+
3cnC9GbHZj/QXwRAlj3dp1CDrMHqvbrCZ6jW7YS/+dVUn24oMadyIX0E88EBgP+l
|
22
|
+
Wu4gvzn2nkV8L4Ukcspt7Mqvr/YGlWu/8GPVACUp1AdZH7YJ7ARZE7SVgxR/iNGd
|
23
|
+
HJclQ/939s63ZNrnsOR+6qrhcC3c83UARw==
|
24
|
+
-----END CERTIFICATE-----
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rack/builder"
|
2
|
+
require "json"
|
2
3
|
|
3
4
|
class IntegrationServer
|
4
5
|
|
@@ -38,15 +39,16 @@ class IntegrationServer
|
|
38
39
|
}
|
39
40
|
end
|
40
41
|
|
41
|
-
map "/
|
42
|
+
map "/inspect_request" do
|
42
43
|
run lambda { |env|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
body = {
|
45
|
+
:soap_action => env["HTTP_SOAPACTION"],
|
46
|
+
:cookie => env["HTTP_COOKIE"],
|
47
|
+
:x_token => env["HTTP_X_TOKEN"],
|
48
|
+
:content_type => env["CONTENT_TYPE"]
|
49
|
+
}
|
50
|
+
|
51
|
+
IntegrationServer.respond_with :body => JSON.dump(body)
|
50
52
|
}
|
51
53
|
end
|
52
54
|
|
data/spec/savon/client_spec.rb
CHANGED
@@ -28,6 +28,20 @@ describe Savon::Client do
|
|
28
28
|
expect(client.globals[:wsdl]).to eq(Fixture.wsdl(:authentication))
|
29
29
|
end
|
30
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
|
+
|
31
45
|
it "raises if not initialized with either a :wsdl or both :endpoint and :namespace options" do
|
32
46
|
expect { Savon.client(:endpoint => "http://example.com") }.
|
33
47
|
to raise_error(Savon::InitializationError, /Expected either a WSDL document or the SOAP endpoint and target namespace options/)
|
@@ -75,29 +89,17 @@ describe Savon::Client do
|
|
75
89
|
wsdl = Wasabi::Document.new('http://example.com')
|
76
90
|
operation = Savon::Operation.new(:authenticate, wsdl, Savon::GlobalOptions.new)
|
77
91
|
operation.expects(:call).with(locals).returns(soap_response)
|
78
|
-
|
92
|
+
|
93
|
+
Savon::Operation.expects(:create).with(
|
94
|
+
:authenticate,
|
95
|
+
instance_of(Wasabi::Document),
|
96
|
+
instance_of(Savon::GlobalOptions)
|
97
|
+
).returns(operation)
|
79
98
|
|
80
99
|
response = new_client.call(:authenticate, locals)
|
81
100
|
expect(response).to eq(soap_response)
|
82
101
|
end
|
83
102
|
|
84
|
-
it "sets the cookies for the next request" do
|
85
|
-
last_response = new_http_response(:headers => { "Set-Cookie" => "some-cookie=choc-chip; Path=/; HttpOnly" })
|
86
|
-
client = new_client
|
87
|
-
|
88
|
-
HTTPI.stubs(:post).returns(last_response)
|
89
|
-
|
90
|
-
# does not try to set cookies for the first request
|
91
|
-
HTTPI::Request.any_instance.expects(:set_cookies).never
|
92
|
-
client.call(:authenticate)
|
93
|
-
|
94
|
-
HTTPI.stubs(:post).returns(new_http_response)
|
95
|
-
|
96
|
-
# sets cookies from the last response
|
97
|
-
HTTPI::Request.any_instance.expects(:set_cookies).with(last_response)
|
98
|
-
client.call(:authenticate)
|
99
|
-
end
|
100
|
-
|
101
103
|
it "supports a block without arguments to call an operation with local options" do
|
102
104
|
client = new_client(:endpoint => @server.url(:repeat))
|
103
105
|
|
@@ -121,6 +123,13 @@ describe Savon::Client do
|
|
121
123
|
expect(response.http.body).to include("<symbol>AAPL</symbol>")
|
122
124
|
end
|
123
125
|
|
126
|
+
it "accepts arguments for the message tag" do
|
127
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
128
|
+
response = client.call(:authenticate, :attributes => { "ID" => "ABC321"})
|
129
|
+
|
130
|
+
expect(response.http.body).to include('<tns:authenticate ID="ABC321">')
|
131
|
+
end
|
132
|
+
|
124
133
|
it "raises when the operation name is not a symbol" do
|
125
134
|
expect { new_client.call("not a symbol") }.to raise_error(
|
126
135
|
ArgumentError,
|
@@ -1,11 +1,25 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "integration/support/server"
|
3
|
+
require "json"
|
4
|
+
require "ostruct"
|
3
5
|
|
4
6
|
describe Savon::Operation do
|
5
7
|
|
6
8
|
let(:globals) { Savon::GlobalOptions.new(:endpoint => @server.url(:repeat), :log => false) }
|
7
|
-
let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:
|
8
|
-
|
9
|
+
let(:wsdl) { Wasabi::Document.new Fixture.wsdl(:taxcloud) }
|
10
|
+
|
11
|
+
let(:no_wsdl) {
|
12
|
+
wsdl = Wasabi::Document.new
|
13
|
+
|
14
|
+
wsdl.endpoint = "http://example.com"
|
15
|
+
wsdl.namespace = "http://v1.example.com"
|
16
|
+
|
17
|
+
wsdl
|
18
|
+
}
|
19
|
+
|
20
|
+
def new_operation(operation_name, wsdl, globals)
|
21
|
+
Savon::Operation.create(operation_name, wsdl, globals)
|
22
|
+
end
|
9
23
|
|
10
24
|
before :all do
|
11
25
|
@server = IntegrationServer.run
|
@@ -17,33 +31,124 @@ describe Savon::Operation do
|
|
17
31
|
|
18
32
|
describe ".create with a WSDL" do
|
19
33
|
it "returns a new operation" do
|
20
|
-
operation =
|
34
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
21
35
|
expect(operation).to be_a(Savon::Operation)
|
22
36
|
end
|
23
37
|
|
24
38
|
it "raises if the operation name is not a Symbol" do
|
25
|
-
expect {
|
39
|
+
expect { new_operation("not a symbol", wsdl, globals) }.
|
26
40
|
to raise_error(ArgumentError, /Expected the first parameter \(the name of the operation to call\) to be a symbol/)
|
27
41
|
end
|
28
42
|
|
29
43
|
it "raises if the operation is not available for the service" do
|
30
|
-
expect {
|
44
|
+
expect { new_operation(:no_such_operation, wsdl, globals) }.
|
31
45
|
to raise_error(ArgumentError, /Unable to find SOAP operation: :no_such_operation/)
|
32
46
|
end
|
33
47
|
end
|
34
48
|
|
35
49
|
describe ".create without a WSDL" do
|
36
50
|
it "returns a new operation" do
|
37
|
-
operation =
|
51
|
+
operation = new_operation(:verify_address, no_wsdl, globals)
|
38
52
|
expect(operation).to be_a(Savon::Operation)
|
39
53
|
end
|
40
54
|
end
|
41
55
|
|
42
56
|
describe "#call" do
|
43
57
|
it "returns a response object" do
|
44
|
-
operation =
|
58
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
45
59
|
expect(operation.call).to be_a(Savon::Response)
|
46
60
|
end
|
61
|
+
|
62
|
+
it "uses the global :endpoint option for the request" do
|
63
|
+
globals.endpoint("http://v1.example.com")
|
64
|
+
HTTPI::Request.any_instance.expects(:url=).with("http://v1.example.com")
|
65
|
+
|
66
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
67
|
+
|
68
|
+
# stub the actual request
|
69
|
+
http_response = HTTPI::Response.new(200, {}, "")
|
70
|
+
operation.expects(:call!).returns(http_response)
|
71
|
+
|
72
|
+
operation.call
|
73
|
+
end
|
74
|
+
|
75
|
+
it "falls back to use the WSDL's endpoint if the :endpoint option was not set" do
|
76
|
+
globals_without_endpoint = Savon::GlobalOptions.new(:log => false)
|
77
|
+
HTTPI::Request.any_instance.expects(:url=).with(wsdl.endpoint)
|
78
|
+
|
79
|
+
operation = new_operation(:verify_address, wsdl, globals_without_endpoint)
|
80
|
+
|
81
|
+
# stub the actual request
|
82
|
+
http_response = HTTPI::Response.new(200, {}, "")
|
83
|
+
operation.expects(:call!).returns(http_response)
|
84
|
+
|
85
|
+
operation.call
|
86
|
+
end
|
87
|
+
|
88
|
+
it "sets the Content-Length header" do
|
89
|
+
# XXX: probably the worst spec ever written. refactor! [dh, 2013-01-05]
|
90
|
+
http_request = HTTPI::Request.new
|
91
|
+
http_request.headers.expects(:[]=).with("Content-Length", "312")
|
92
|
+
Savon::SOAPRequest.any_instance.expects(:build).returns(http_request)
|
93
|
+
|
94
|
+
new_operation(:verify_address, wsdl, globals).call
|
95
|
+
end
|
96
|
+
|
97
|
+
it "passes the local :soap_action option to the request builder" do
|
98
|
+
globals.endpoint @server.url(:inspect_request)
|
99
|
+
soap_action = "http://v1.example.com/VerifyAddress"
|
100
|
+
|
101
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
102
|
+
response = operation.call(:soap_action => soap_action)
|
103
|
+
|
104
|
+
actual_soap_action = inspect_request(response).soap_action
|
105
|
+
expect(actual_soap_action).to eq(%("#{soap_action}"))
|
106
|
+
end
|
107
|
+
|
108
|
+
it "uses the local :cookies option" do
|
109
|
+
globals.endpoint @server.url(:inspect_request)
|
110
|
+
cookies = [HTTPI::Cookie.new("some-cookie=choc-chip")]
|
111
|
+
|
112
|
+
HTTPI::Request.any_instance.expects(:set_cookies).with(cookies)
|
113
|
+
|
114
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
115
|
+
operation.call(:cookies => cookies)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "passes nil to the request builder if the :soap_action was set to nil" do
|
119
|
+
globals.endpoint @server.url(:inspect_request)
|
120
|
+
|
121
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
122
|
+
response = operation.call(:soap_action => nil)
|
123
|
+
|
124
|
+
actual_soap_action = inspect_request(response).soap_action
|
125
|
+
expect(actual_soap_action).to be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
it "gets the SOAP action from the WSDL if available" do
|
129
|
+
globals.endpoint @server.url(:inspect_request)
|
130
|
+
|
131
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
132
|
+
response = operation.call
|
133
|
+
|
134
|
+
actual_soap_action = inspect_request(response).soap_action
|
135
|
+
expect(actual_soap_action).to eq('"http://taxcloud.net/VerifyAddress"')
|
136
|
+
end
|
137
|
+
|
138
|
+
it "falls back to Gyoku if both option and WSDL are not available" do
|
139
|
+
globals.endpoint @server.url(:inspect_request)
|
140
|
+
|
141
|
+
operation = new_operation(:authenticate, no_wsdl, globals)
|
142
|
+
response = operation.call
|
143
|
+
|
144
|
+
actual_soap_action = inspect_request(response).soap_action
|
145
|
+
expect(actual_soap_action).to eq(%("authenticate"))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def inspect_request(response)
|
150
|
+
hash = JSON.parse(response.http.body)
|
151
|
+
OpenStruct.new(hash)
|
47
152
|
end
|
48
153
|
|
49
154
|
end
|
data/spec/savon/options_spec.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "integration/support/server"
|
3
|
+
require "json"
|
4
|
+
require "ostruct"
|
5
|
+
require "logger"
|
3
6
|
|
4
7
|
describe "Options" do
|
5
8
|
|
@@ -34,6 +37,16 @@ describe "Options" do
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
40
|
+
context "global :namespaces" do
|
41
|
+
it "adds additional namespaces to the SOAP envelope" do
|
42
|
+
namespaces = { "xmlns:whatever" => "http://whatever.example.com" }
|
43
|
+
client = new_client(:endpoint => @server.url(:repeat), :namespaces => namespaces)
|
44
|
+
response = client.call(:authenticate)
|
45
|
+
|
46
|
+
expect(response.http.body).to include('xmlns:whatever="http://whatever.example.com"')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
37
50
|
context "global :proxy" do
|
38
51
|
it "sets the proxy server to use" do
|
39
52
|
proxy_url = "http://example.com"
|
@@ -48,10 +61,12 @@ describe "Options" do
|
|
48
61
|
|
49
62
|
context "global :headers" do
|
50
63
|
it "sets the HTTP headers for the next request" do
|
51
|
-
client = new_client(:endpoint => @server.url(:
|
64
|
+
client = new_client(:endpoint => @server.url(:inspect_request), :headers => { "X-Token" => "secret" })
|
52
65
|
|
53
66
|
response = client.call(:authenticate)
|
54
|
-
|
67
|
+
x_token = inspect_request(response).x_token
|
68
|
+
|
69
|
+
expect(x_token).to eq("secret")
|
55
70
|
end
|
56
71
|
end
|
57
72
|
|
@@ -60,8 +75,16 @@ describe "Options" do
|
|
60
75
|
non_routable_ip = "http://10.255.255.1"
|
61
76
|
client = new_client(:endpoint => non_routable_ip, :open_timeout => 1)
|
62
77
|
|
63
|
-
|
64
|
-
|
78
|
+
expect { client.call(:authenticate) }.to raise_error { |error|
|
79
|
+
if error.kind_of? Errno::EHOSTUNREACH
|
80
|
+
warn "Warning: looks like your network may be down?!\n" +
|
81
|
+
"-> skipping spec at #{__FILE__}:#{__LINE__}"
|
82
|
+
else
|
83
|
+
# TODO: make HTTPI tag timeout errors, then depend on HTTPI::TimeoutError
|
84
|
+
# instead of a specific client error [dh, 2012-12-08]
|
85
|
+
expect(error).to be_an(HTTPClient::ConnectTimeoutError)
|
86
|
+
end
|
87
|
+
}
|
65
88
|
end
|
66
89
|
end
|
67
90
|
|
@@ -69,7 +92,8 @@ describe "Options" do
|
|
69
92
|
it "makes the client timeout after n seconds" do
|
70
93
|
client = new_client(:endpoint => @server.url(:timeout), :open_timeout => 1, :read_timeout => 1)
|
71
94
|
|
72
|
-
expect { client.call(:authenticate) }.
|
95
|
+
expect { client.call(:authenticate) }.
|
96
|
+
to raise_error(HTTPClient::ReceiveTimeoutError)
|
73
97
|
end
|
74
98
|
end
|
75
99
|
|
@@ -82,11 +106,11 @@ describe "Options" do
|
|
82
106
|
end
|
83
107
|
|
84
108
|
it "changes the Content-Type header" do
|
85
|
-
client = new_client(:endpoint => @server.url(:
|
86
|
-
:headers => { "Inspect" => "CONTENT_TYPE" })
|
109
|
+
client = new_client(:endpoint => @server.url(:inspect_request), :encoding => "ISO-8859-1")
|
87
110
|
|
88
111
|
response = client.call(:authenticate)
|
89
|
-
|
112
|
+
content_type = inspect_request(response).content_type
|
113
|
+
expect(content_type).to eq("text/xml;charset=ISO-8859-1")
|
90
114
|
end
|
91
115
|
end
|
92
116
|
|
@@ -115,14 +139,6 @@ describe "Options" do
|
|
115
139
|
expect(response.http.body).to include("<user>lea</user>")
|
116
140
|
expect(response.http.body).to include("<password>top-secret</password>")
|
117
141
|
end
|
118
|
-
|
119
|
-
it "allows overwriting the SOAPAction HTTP header" do
|
120
|
-
client = new_client(:endpoint => @server.url(:inspect_header),
|
121
|
-
:headers => { "Inspect" => "HTTP_SOAPACTION" })
|
122
|
-
|
123
|
-
response = client.call(:authenticate)
|
124
|
-
expect(response.http.body).to eq('"authenticate"')
|
125
|
-
end
|
126
142
|
end
|
127
143
|
|
128
144
|
context "global :env_namespace" do
|
@@ -195,15 +211,43 @@ describe "Options" do
|
|
195
211
|
end
|
196
212
|
|
197
213
|
context "global :log" do
|
198
|
-
it "
|
214
|
+
it "instructs Savon not to log SOAP requests and responses" do
|
215
|
+
stdout = mock_stdout {
|
216
|
+
client = new_client(:endpoint => @server.url, :log => false)
|
217
|
+
client.call(:authenticate)
|
218
|
+
}
|
219
|
+
|
220
|
+
expect(stdout.string).to be_empty
|
221
|
+
end
|
222
|
+
|
223
|
+
it "silences HTTPI as well" do
|
199
224
|
HTTPI.expects(:log=).with(false)
|
200
225
|
new_client(:log => false)
|
201
226
|
end
|
202
227
|
|
228
|
+
it "instructs Savon to log SOAP requests and responses" do
|
229
|
+
stdout = mock_stdout {
|
230
|
+
client = new_client(:endpoint => @server.url, :log => true)
|
231
|
+
client.call(:authenticate)
|
232
|
+
}
|
233
|
+
|
234
|
+
expect(stdout.string).to include("INFO -- : SOAP request")
|
235
|
+
end
|
236
|
+
|
203
237
|
it "turns HTTPI logging back on as well" do
|
204
238
|
HTTPI.expects(:log=).with(true)
|
205
239
|
new_client(:log => true)
|
206
240
|
end
|
241
|
+
|
242
|
+
def mock_stdout
|
243
|
+
stdout = StringIO.new
|
244
|
+
$stdout = stdout
|
245
|
+
|
246
|
+
yield
|
247
|
+
|
248
|
+
$stdout = STDOUT
|
249
|
+
stdout
|
250
|
+
end
|
207
251
|
end
|
208
252
|
|
209
253
|
context "global :logger" do
|
@@ -211,6 +255,15 @@ describe "Options" do
|
|
211
255
|
logger = new_client.globals[:logger]
|
212
256
|
expect(logger).to be_a(Logger)
|
213
257
|
end
|
258
|
+
|
259
|
+
it "allows a custom logger to be set" do
|
260
|
+
custom_logger = Logger.new($stdout)
|
261
|
+
|
262
|
+
client = new_client(:logger => custom_logger, :log => true)
|
263
|
+
logger = client.globals[:logger]
|
264
|
+
|
265
|
+
expect(logger).to eq(custom_logger)
|
266
|
+
end
|
214
267
|
end
|
215
268
|
|
216
269
|
context "global :log_level" do
|
@@ -257,7 +310,7 @@ describe "Options" do
|
|
257
310
|
|
258
311
|
context "global :ssl_version" do
|
259
312
|
it "sets the SSL version to use" do
|
260
|
-
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:SSLv3)
|
313
|
+
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:SSLv3).twice
|
261
314
|
|
262
315
|
client = new_client(:endpoint => @server.url, :ssl_version => :SSLv3)
|
263
316
|
client.call(:authenticate)
|
@@ -266,7 +319,7 @@ describe "Options" do
|
|
266
319
|
|
267
320
|
context "global :ssl_verify_mode" do
|
268
321
|
it "sets the verify mode to use" do
|
269
|
-
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:none)
|
322
|
+
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:none).twice
|
270
323
|
|
271
324
|
client = new_client(:endpoint => @server.url, :ssl_verify_mode => :none)
|
272
325
|
client.call(:authenticate)
|
@@ -276,17 +329,30 @@ describe "Options" do
|
|
276
329
|
context "global :ssl_cert_key_file" do
|
277
330
|
it "sets the cert key file to use" do
|
278
331
|
cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
|
279
|
-
HTTPI::Auth::SSL.any_instance.expects(:cert_key_file=).with(cert_key)
|
332
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_key_file=).with(cert_key).twice
|
280
333
|
|
281
334
|
client = new_client(:endpoint => @server.url, :ssl_cert_key_file => cert_key)
|
282
335
|
client.call(:authenticate)
|
283
336
|
end
|
284
337
|
end
|
285
338
|
|
339
|
+
context "global :ssl_cert_key_password" do
|
340
|
+
it "sets the encrypted cert key file password to use" do
|
341
|
+
cert_key = File.expand_path("../../fixtures/ssl/client_encrypted_key.pem", __FILE__)
|
342
|
+
cert_key_pass = "secure-password!42"
|
343
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_key_file=).with(cert_key).twice
|
344
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_key_password=).with(cert_key_pass).twice
|
345
|
+
|
346
|
+
client = new_client(:endpoint => @server.url, :ssl_cert_key_file => cert_key, :ssl_cert_key_password => cert_key_pass)
|
347
|
+
client.call(:authenticate)
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
286
352
|
context "global :ssl_cert_file" do
|
287
353
|
it "sets the cert file to use" do
|
288
354
|
cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
|
289
|
-
HTTPI::Auth::SSL.any_instance.expects(:cert_file=).with(cert)
|
355
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_file=).with(cert).twice
|
290
356
|
|
291
357
|
client = new_client(:endpoint => @server.url, :ssl_cert_file => cert)
|
292
358
|
client.call(:authenticate)
|
@@ -296,7 +362,7 @@ describe "Options" do
|
|
296
362
|
context "global :ssl_ca_cert_file" do
|
297
363
|
it "sets the ca cert file to use" do
|
298
364
|
ca_cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
|
299
|
-
HTTPI::Auth::SSL.any_instance.expects(:ca_cert_file=).with(ca_cert)
|
365
|
+
HTTPI::Auth::SSL.any_instance.expects(:ca_cert_file=).with(ca_cert).twice
|
300
366
|
|
301
367
|
client = new_client(:endpoint => @server.url, :ssl_ca_cert_file => ca_cert)
|
302
368
|
client.call(:authenticate)
|
@@ -513,21 +579,30 @@ describe "Options" do
|
|
513
579
|
end
|
514
580
|
end
|
515
581
|
|
582
|
+
context "request: attributes" do
|
583
|
+
it "when set, adds the attributes to the message tag" do
|
584
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
585
|
+
response = client.call(:authenticate, :attributes => { "Token" => "secret"})
|
586
|
+
|
587
|
+
expect(response.http.body).to include('<tns:authenticate Token="secret">')
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
516
591
|
context "request: soap_action" do
|
517
592
|
it "without it, Savon tries to get the SOAPAction from the WSDL document and falls back to Gyoku" do
|
518
|
-
client = new_client(:endpoint => @server.url(:
|
519
|
-
:headers => { "Inspect" => "HTTP_SOAPACTION" })
|
593
|
+
client = new_client(:endpoint => @server.url(:inspect_request))
|
520
594
|
|
521
595
|
response = client.call(:authenticate)
|
522
|
-
|
596
|
+
soap_action = inspect_request(response).soap_action
|
597
|
+
expect(soap_action).to eq('"authenticate"')
|
523
598
|
end
|
524
599
|
|
525
600
|
it "when set, changes the SOAPAction HTTP header" do
|
526
|
-
client = new_client(:endpoint => @server.url(:
|
527
|
-
:headers => { "Inspect" => "HTTP_SOAPACTION" })
|
601
|
+
client = new_client(:endpoint => @server.url(:inspect_request))
|
528
602
|
|
529
603
|
response = client.call(:authenticate, :soap_action => "doAuthenticate")
|
530
|
-
|
604
|
+
soap_action = inspect_request(response).soap_action
|
605
|
+
expect(soap_action).to eq('"doAuthenticate"')
|
531
606
|
end
|
532
607
|
end
|
533
608
|
|
@@ -553,6 +628,24 @@ describe "Options" do
|
|
553
628
|
end
|
554
629
|
end
|
555
630
|
|
631
|
+
context "request :cookies" do
|
632
|
+
it "accepts an Array of HTTPI::Cookie objects for the next request" do
|
633
|
+
cookies = [
|
634
|
+
HTTPI::Cookie.new("some-cookie=choc-chip"),
|
635
|
+
HTTPI::Cookie.new("another-cookie=ny-cheesecake")
|
636
|
+
]
|
637
|
+
|
638
|
+
client = new_client(:endpoint => @server.url(:inspect_request))
|
639
|
+
response = client.call(:authenticate, :cookies => cookies)
|
640
|
+
|
641
|
+
cookie = inspect_request(response).cookie
|
642
|
+
expect(cookie.split(";")).to include(
|
643
|
+
"some-cookie=choc-chip",
|
644
|
+
"another-cookie=ny-cheesecake"
|
645
|
+
)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
556
649
|
context "request :advanced_typecasting" do
|
557
650
|
it "can be changed to false to disable Nori's advanced typecasting" do
|
558
651
|
client = new_client(:endpoint => @server.url(:repeat))
|
@@ -584,4 +677,9 @@ describe "Options" do
|
|
584
677
|
Savon.client(globals, &block)
|
585
678
|
end
|
586
679
|
|
680
|
+
def inspect_request(response)
|
681
|
+
hash = JSON.parse(response.http.body)
|
682
|
+
OpenStruct.new(hash)
|
683
|
+
end
|
684
|
+
|
587
685
|
end
|