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