savon 2.2.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +20 -9
- data/CHANGELOG.md +157 -10
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +10 -2
- data/README.md +38 -13
- data/donate.png +0 -0
- data/lib/savon/builder.rb +81 -15
- data/lib/savon/client.rb +6 -2
- data/lib/savon/core_ext/string.rb +0 -1
- data/lib/savon/header.rb +68 -17
- data/lib/savon/log_message.rb +7 -3
- data/lib/savon/message.rb +6 -7
- data/lib/savon/mock/expectation.rb +12 -2
- data/lib/savon/model.rb +4 -0
- data/lib/savon/operation.rb +45 -38
- data/lib/savon/options.rb +149 -22
- data/lib/savon/qualified_message.rb +31 -25
- data/lib/savon/request.rb +24 -4
- data/lib/savon/request_logger.rb +48 -0
- data/lib/savon/response.rb +35 -18
- data/lib/savon/soap_fault.rb +11 -11
- data/lib/savon/version.rb +1 -3
- data/savon.gemspec +12 -11
- data/spec/fixtures/response/empty_soap_fault.xml +13 -0
- data/spec/fixtures/response/f5.xml +39 -0
- data/spec/fixtures/response/no_body.xml +1 -0
- data/spec/fixtures/response/soap_fault_funky.xml +8 -0
- data/spec/fixtures/wsdl/brand.xml +624 -0
- data/spec/fixtures/wsdl/elements_in_types.xml +43 -0
- data/spec/fixtures/wsdl/no_message_tag.xml +1267 -0
- data/spec/fixtures/wsdl/vies.xml +176 -0
- data/spec/integration/centra_spec.rb +67 -0
- data/spec/integration/email_example_spec.rb +1 -1
- data/spec/integration/random_quote_spec.rb +23 -0
- data/spec/integration/stockquote_example_spec.rb +7 -1
- data/spec/integration/support/application.rb +1 -1
- data/spec/integration/zipcode_example_spec.rb +1 -1
- data/spec/savon/builder_spec.rb +50 -0
- data/spec/savon/client_spec.rb +78 -0
- data/spec/savon/core_ext/string_spec.rb +9 -9
- data/spec/savon/features/message_tag_spec.rb +5 -0
- data/spec/savon/http_error_spec.rb +2 -2
- data/spec/savon/log_message_spec.rb +18 -1
- data/spec/savon/message_spec.rb +70 -0
- data/spec/savon/mock_spec.rb +31 -0
- data/spec/savon/model_spec.rb +28 -0
- data/spec/savon/operation_spec.rb +69 -3
- data/spec/savon/options_spec.rb +515 -87
- data/spec/savon/qualified_message_spec.rb +101 -0
- data/spec/savon/request_logger_spec.rb +37 -0
- data/spec/savon/request_spec.rb +85 -10
- data/spec/savon/response_spec.rb +118 -27
- data/spec/savon/soap_fault_spec.rb +25 -5
- data/spec/savon/softlayer_spec.rb +27 -0
- data/spec/spec_helper.rb +5 -2
- data/spec/support/adapters.rb +48 -0
- data/spec/support/integration.rb +1 -1
- metadata +76 -93
@@ -4,33 +4,33 @@ describe String do
|
|
4
4
|
|
5
5
|
describe "snakecase" do
|
6
6
|
it "lowercases one word CamelCase" do
|
7
|
-
"Merb".snakecase.
|
7
|
+
expect("Merb".snakecase).to eq("merb")
|
8
8
|
end
|
9
9
|
|
10
10
|
it "makes one underscore snakecase two word CamelCase" do
|
11
|
-
"MerbCore".snakecase.
|
11
|
+
expect("MerbCore".snakecase).to eq("merb_core")
|
12
12
|
end
|
13
13
|
|
14
14
|
it "handles CamelCase with more than 2 words" do
|
15
|
-
"SoYouWantContributeToMerbCore".snakecase.
|
15
|
+
expect("SoYouWantContributeToMerbCore".snakecase).to eq("so_you_want_contribute_to_merb_core")
|
16
16
|
end
|
17
17
|
|
18
18
|
it "handles CamelCase with more than 2 capital letter in a row" do
|
19
|
-
"CNN".snakecase.
|
20
|
-
"CNNNews".snakecase.
|
21
|
-
"HeadlineCNNNews".snakecase.
|
19
|
+
expect("CNN".snakecase).to eq("cnn")
|
20
|
+
expect("CNNNews".snakecase).to eq("cnn_news")
|
21
|
+
expect("HeadlineCNNNews".snakecase).to eq("headline_cnn_news")
|
22
22
|
end
|
23
23
|
|
24
24
|
it "does NOT change one word lowercase" do
|
25
|
-
"merb".snakecase.
|
25
|
+
expect("merb".snakecase).to eq("merb")
|
26
26
|
end
|
27
27
|
|
28
28
|
it "leaves snake_case as is" do
|
29
|
-
"merb_core".snakecase.
|
29
|
+
expect("merb_core".snakecase).to eq("merb_core")
|
30
30
|
end
|
31
31
|
|
32
32
|
it "converts period characters to underscores" do
|
33
|
-
"User.GetEmail".snakecase.
|
33
|
+
expect("User.GetEmail".snakecase).to eq("user_get_email")
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -27,6 +27,11 @@ describe Savon do
|
|
27
27
|
expect(message_tag).to eq(['http://www.betfair.com/publicapi/v5/BFExchangeService/', 'getBet'])
|
28
28
|
end
|
29
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
|
+
|
30
35
|
it 'knows the message tag for :wasmuth' do
|
31
36
|
message_tag = message_tag_for(:wasmuth, :get_st_tables)
|
32
37
|
expect(message_tag).to eq(['http://ws.online.msw/', 'getStTables'])
|
@@ -11,11 +11,11 @@ describe Savon::HTTPError do
|
|
11
11
|
describe ".present?" do
|
12
12
|
it "returns true if there was an HTTP error" do
|
13
13
|
http = new_response(:code => 404, :body => "Not Found")
|
14
|
-
expect(Savon::HTTPError.present? http).to
|
14
|
+
expect(Savon::HTTPError.present? http).to be_truthy
|
15
15
|
end
|
16
16
|
|
17
17
|
it "returns false unless there was an HTTP error" do
|
18
|
-
expect(Savon::HTTPError.present? new_response).to
|
18
|
+
expect(Savon::HTTPError.present? new_response).to be_falsey
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -21,9 +21,26 @@ describe Savon::LogMessage do
|
|
21
21
|
expect(message).to include("\n <body>")
|
22
22
|
end
|
23
23
|
|
24
|
-
it "filters tags in a given message" do
|
24
|
+
it "filters tags in a given message without pretty printing" do
|
25
25
|
message = log_message("<root><password>secret</password></root>", [:password], false).to_s
|
26
26
|
expect(message).to include("<password>***FILTERED***</password>")
|
27
|
+
expect(message).to_not include("\n <password>***FILTERED***</password>") # no pretty printing
|
28
|
+
end
|
29
|
+
|
30
|
+
it "filters tags in a given message with pretty printing" do
|
31
|
+
message = log_message("<root><password>secret</password></root>", [:password], true).to_s
|
32
|
+
expect(message).to include("\n <password>***FILTERED***</password>")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "properly applies Proc filter" do
|
36
|
+
filter = Proc.new do |document|
|
37
|
+
document.xpath('//password').each do |node|
|
38
|
+
node.content = "FILTERED"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
message = log_message("<root><password>secret</password></root>", [filter], false).to_s
|
43
|
+
expect(message).to include("<password>FILTERED</password>")
|
27
44
|
end
|
28
45
|
|
29
46
|
def log_message(*args)
|
@@ -0,0 +1,70 @@
|
|
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
|
+
let(:client_config) {
|
15
|
+
{
|
16
|
+
:endpoint => @server.url(:repeat),
|
17
|
+
:namespace => 'http://example.com',
|
18
|
+
:log => false,
|
19
|
+
|
20
|
+
:element_form_default => :qualified,
|
21
|
+
:convert_request_keys_to => :camelcase,
|
22
|
+
|
23
|
+
:convert_response_tags_to => nil
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:client) { Savon.client(client_config) }
|
28
|
+
|
29
|
+
context "with a qualified message" do
|
30
|
+
let(:message) {
|
31
|
+
{
|
32
|
+
:email_count => 3,
|
33
|
+
:user_name => 'josh',
|
34
|
+
:order! => [:user_name, :email_count]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
let(:converted_keys) {
|
39
|
+
'<wsdl:UserName>josh</wsdl:UserName><wsdl:EmailCount>3</wsdl:EmailCount>'
|
40
|
+
}
|
41
|
+
it "converts request Hash keys for which there is not namespace" do
|
42
|
+
response = client.call(:something, :message => message)
|
43
|
+
expect(response.xml).to include(converted_keys)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'use_wsa_headers' do
|
48
|
+
let(:client_config) { super().merge(use_wsa_headers: true) }
|
49
|
+
|
50
|
+
context 'headers' do
|
51
|
+
[ 'wsa:Action', 'wsa:To', 'wsa:MessageID' ].each do |header|
|
52
|
+
it "should include #{header} header" do
|
53
|
+
response = client.call(:something, message: {})
|
54
|
+
expect(response.xml).to include(header)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
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
|
+
end
|
69
|
+
|
70
|
+
end
|
data/spec/savon/mock_spec.rb
CHANGED
@@ -23,6 +23,17 @@ describe "Savon's mock interface" do
|
|
23
23
|
expect(response.http.body).to eq("<fixture/>")
|
24
24
|
end
|
25
25
|
|
26
|
+
it "can verify a request with any parameters and return a fixture response" do
|
27
|
+
message = { :username => "luke", :password => :any }
|
28
|
+
savon.expects(:authenticate).with(:message => message).returns("<fixture/>")
|
29
|
+
|
30
|
+
response = new_client.call(:authenticate) do
|
31
|
+
message(:username => "luke", :password => "secret")
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(response.http.body).to eq("<fixture/>")
|
35
|
+
end
|
36
|
+
|
26
37
|
it "accepts a Hash to specify the response code, headers and body" do
|
27
38
|
soap_fault = Fixture.response(:soap_fault)
|
28
39
|
response = { :code => 500, :headers => { "X-Result" => "invalid" }, :body => soap_fault }
|
@@ -114,6 +125,26 @@ describe "Savon's mock interface" do
|
|
114
125
|
" with this message: #{message.inspect}")
|
115
126
|
end
|
116
127
|
|
128
|
+
it "does not fail when any message is expected and an actual message" do
|
129
|
+
savon.expects(:find_user).with(:message => :any).returns("<fixture/>")
|
130
|
+
message = { :username => "luke" }
|
131
|
+
|
132
|
+
expect { new_client.call(:find_user, :message => message) }.to_not raise_error
|
133
|
+
end
|
134
|
+
|
135
|
+
it "does not fail when any message is expected and no actual message" do
|
136
|
+
savon.expects(:find_user).with(:message => :any).returns("<fixture/>")
|
137
|
+
|
138
|
+
expect { new_client.call(:find_user) }.to_not raise_error
|
139
|
+
end
|
140
|
+
|
141
|
+
it "matchers can be used to specify the message" do
|
142
|
+
savon.expects(:find_user).with(:message => include(:username)).returns("<fixture/>")
|
143
|
+
message = { :username => "Han Solo", password: "querty"}
|
144
|
+
|
145
|
+
expect { new_client.call(:find_user, :message => message) }.to_not raise_error
|
146
|
+
end
|
147
|
+
|
117
148
|
it "allows code to rescue Savon::Error and still report test failures" do
|
118
149
|
message = { :username => "luke" }
|
119
150
|
savon.expects(:find_user).with(:message => message).returns("<fixture/>")
|
data/spec/savon/model_spec.rb
CHANGED
@@ -151,4 +151,32 @@ describe Savon::Model do
|
|
151
151
|
supermodel.authenticate(:message => { :username => "luke", :password => "secret" })
|
152
152
|
end
|
153
153
|
|
154
|
+
describe ".all_operations" do
|
155
|
+
it "should call operations with all available client operations" do
|
156
|
+
model = Class.new {
|
157
|
+
extend Savon::Model
|
158
|
+
|
159
|
+
client :wsdl => Fixture.wsdl(:taxcloud)
|
160
|
+
all_operations
|
161
|
+
}
|
162
|
+
|
163
|
+
[:verify_address,
|
164
|
+
:lookup_for_date,
|
165
|
+
:lookup,
|
166
|
+
:authorized,
|
167
|
+
:authorized_with_capture,
|
168
|
+
:captured,
|
169
|
+
:returned,
|
170
|
+
:get_tic_groups,
|
171
|
+
:get_ti_cs,
|
172
|
+
:get_ti_cs_by_group,
|
173
|
+
:add_exempt_certificate,
|
174
|
+
:delete_exempt_certificate,
|
175
|
+
:get_exempt_certificates].each do |method|
|
176
|
+
expect(model).to respond_to(method)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
154
182
|
end
|
@@ -44,6 +44,16 @@ describe Savon::Operation do
|
|
44
44
|
expect { new_operation(:no_such_operation, wsdl, globals) }.
|
45
45
|
to raise_error(Savon::UnknownOperationError, /Unable to find SOAP operation: :no_such_operation/)
|
46
46
|
end
|
47
|
+
|
48
|
+
it "raises if the endpoint cannot be reached" do
|
49
|
+
message = "Error!"
|
50
|
+
response = HTTPI::Response.new(500, {}, message)
|
51
|
+
error = Wasabi::Resolver::HTTPError.new(message, response)
|
52
|
+
Wasabi::Document.any_instance.stubs(:soap_actions).raises(error)
|
53
|
+
|
54
|
+
expect { new_operation(:verify_address, wsdl, globals) }.
|
55
|
+
to raise_error(Savon::HTTPError, /#{message}/)
|
56
|
+
end
|
47
57
|
end
|
48
58
|
|
49
59
|
describe ".create without a WSDL" do
|
@@ -77,7 +87,7 @@ describe Savon::Operation do
|
|
77
87
|
|
78
88
|
# stub the actual request
|
79
89
|
http_response = HTTPI::Response.new(200, {}, "")
|
80
|
-
operation.expects(:
|
90
|
+
operation.expects(:call_with_logging).returns(http_response)
|
81
91
|
|
82
92
|
operation.call
|
83
93
|
end
|
@@ -90,7 +100,7 @@ describe Savon::Operation do
|
|
90
100
|
|
91
101
|
# stub the actual request
|
92
102
|
http_response = HTTPI::Response.new(200, {}, "")
|
93
|
-
operation.expects(:
|
103
|
+
operation.expects(:call_with_logging).returns(http_response)
|
94
104
|
|
95
105
|
operation.call
|
96
106
|
end
|
@@ -154,11 +164,67 @@ describe Savon::Operation do
|
|
154
164
|
actual_soap_action = inspect_request(response).soap_action
|
155
165
|
expect(actual_soap_action).to eq(%("authenticate"))
|
156
166
|
end
|
167
|
+
|
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)
|
176
|
+
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
|
+
|
184
|
+
expect(response).to be_a(Savon::Multipart::Response)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
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)
|
192
|
+
|
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/
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe "#request" do
|
206
|
+
it "returns the request" do
|
207
|
+
operation = new_operation(:verify_address, wsdl, globals)
|
208
|
+
request = operation.request
|
209
|
+
|
210
|
+
expect(request.body).to include('<tns:VerifyAddress></tns:VerifyAddress>')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
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
|
157
224
|
end
|
158
225
|
|
159
226
|
def inspect_request(response)
|
160
227
|
hash = JSON.parse(response.http.body)
|
161
228
|
OpenStruct.new(hash)
|
162
229
|
end
|
163
|
-
|
164
230
|
end
|
data/spec/savon/options_spec.rb
CHANGED
@@ -45,6 +45,35 @@ describe "Options" do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
context "global: :no_message_tag" do
|
49
|
+
it "omits the 'message tag' encapsulation step" do
|
50
|
+
client = new_client(:endpoint => @server.url(:repeat), :no_message_tag => true,
|
51
|
+
:wsdl => Fixture.wsdl(:no_message_tag))
|
52
|
+
msg = {'extLoginData' => {'Login' => 'test.user', 'Password' => 'secret', 'FacilityID' => 1,
|
53
|
+
'ThreePLKey' => '{XXXX-XXXX-XXXX-XXXX}', 'ThreePLID' => 1},
|
54
|
+
'Items' => ['Item' => {'SKU' => '001002003A', 'CustomerID' => 1,
|
55
|
+
'InventoryMethod' => 'FIFO', 'UPC' => '001002003A'}]}
|
56
|
+
response = client.call(:create_items, :message => msg)
|
57
|
+
|
58
|
+
expect(response.http.body.scan(/<tns:extLoginData>/).count).to eq(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "includes the 'message tag' encapsulation step" do
|
62
|
+
# This test is probably just exposing a bug while the previous
|
63
|
+
# test is using a workaround fix.
|
64
|
+
# That is just a guess though. I don't really have to properly debug the WSDL parser.
|
65
|
+
client = new_client(:endpoint => @server.url(:repeat), :no_message_tag => false,
|
66
|
+
:wsdl => Fixture.wsdl(:no_message_tag))
|
67
|
+
msg = {'extLoginData' => {'Login' => 'test.user', 'Password' => 'secret', 'FacilityID' => 1,
|
68
|
+
'ThreePLKey' => '{XXXX-XXXX-XXXX-XXXX}', 'ThreePLID' => 1},
|
69
|
+
'Items' => ['Item' => {'SKU' => '001002003A', 'CustomerID' => 1,
|
70
|
+
'InventoryMethod' => 'FIFO', 'UPC' => '001002003A'}]}
|
71
|
+
response = client.call(:create_items, :message => msg)
|
72
|
+
|
73
|
+
expect(response.http.body.scan(/<tns:extLoginData>/).count).to eq(2)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
48
77
|
context "global :namespaces" do
|
49
78
|
it "adds additional namespaces to the SOAP envelope" do
|
50
79
|
namespaces = { "xmlns:whatever" => "http://whatever.example.com" }
|
@@ -55,6 +84,24 @@ describe "Options" do
|
|
55
84
|
end
|
56
85
|
end
|
57
86
|
|
87
|
+
context 'global :follow_redirects' do
|
88
|
+
it 'sets whether or not request should follow redirects' do
|
89
|
+
client = new_client(:endpoint => @server.url, :follow_redirects => true)
|
90
|
+
|
91
|
+
HTTPI::Request.any_instance.expects(:follow_redirect=).with(true)
|
92
|
+
|
93
|
+
response = client.call(:authenticate)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'defaults to false' do
|
97
|
+
client = new_client(:endpoint => @server.url)
|
98
|
+
|
99
|
+
HTTPI::Request.any_instance.expects(:follow_redirect=).with(false)
|
100
|
+
|
101
|
+
response = client.call(:authenticate)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
58
105
|
context "global :proxy" do
|
59
106
|
it "sets the proxy server to use" do
|
60
107
|
proxy_url = "http://example.com"
|
@@ -67,6 +114,15 @@ describe "Options" do
|
|
67
114
|
end
|
68
115
|
end
|
69
116
|
|
117
|
+
context "global :host" do
|
118
|
+
it "overrides the WSDL endpoint host" do
|
119
|
+
client = new_client(:wsdl => Fixture.wsdl(:no_message_tag), host: "https://example.com:8080")
|
120
|
+
|
121
|
+
request = client.build_request(:update_orders)
|
122
|
+
expect(request.url.to_s).to eq "https://example.com:8080/webserviceexternal/contracts.asmx"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
70
126
|
context "global :headers" do
|
71
127
|
it "sets the HTTP headers for the next request" do
|
72
128
|
client = new_client(:endpoint => @server.url(:inspect_request), :headers => { "X-Token" => "secret" })
|
@@ -80,11 +136,14 @@ describe "Options" do
|
|
80
136
|
|
81
137
|
context "global :open_timeout" do
|
82
138
|
it "makes the client timeout after n seconds" do
|
83
|
-
non_routable_ip = "http://
|
139
|
+
non_routable_ip = "http://192.0.2.0"
|
84
140
|
client = new_client(:endpoint => non_routable_ip, :open_timeout => 0.1)
|
85
141
|
|
86
142
|
expect { client.call(:authenticate) }.to raise_error { |error|
|
87
|
-
|
143
|
+
host_unreachable = error.kind_of? Errno::EHOSTUNREACH
|
144
|
+
net_unreachable = error.kind_of? Errno::ENETUNREACH
|
145
|
+
socket_err = error.kind_of? SocketError
|
146
|
+
if host_unreachable || net_unreachable || socket_err
|
88
147
|
warn "Warning: looks like your network may be down?!\n" +
|
89
148
|
"-> skipping spec at #{__FILE__}:#{__LINE__}"
|
90
149
|
else
|
@@ -125,10 +184,23 @@ describe "Options" do
|
|
125
184
|
context "global :soap_header" do
|
126
185
|
it "accepts a Hash of SOAP header information" do
|
127
186
|
client = new_client(:endpoint => @server.url(:repeat), :soap_header => { :auth_token => "secret" })
|
128
|
-
|
129
187
|
response = client.call(:authenticate)
|
188
|
+
|
130
189
|
expect(response.http.body).to include("<env:Header><authToken>secret</authToken></env:Header>")
|
131
190
|
end
|
191
|
+
|
192
|
+
it "accepts anything other than a String and calls #to_s on it" do
|
193
|
+
to_s_header = Class.new {
|
194
|
+
def to_s
|
195
|
+
"to_s_header"
|
196
|
+
end
|
197
|
+
}.new
|
198
|
+
|
199
|
+
client = new_client(:endpoint => @server.url(:repeat), :soap_header => to_s_header)
|
200
|
+
response = client.call(:authenticate)
|
201
|
+
|
202
|
+
expect(response.http.body).to include("<env:Header>to_s_header</env:Header>")
|
203
|
+
end
|
132
204
|
end
|
133
205
|
|
134
206
|
context "global :element_form_default" do
|
@@ -147,6 +219,17 @@ describe "Options" do
|
|
147
219
|
expect(response.http.body).to include("<user>lea</user>")
|
148
220
|
expect(response.http.body).to include("<password>top-secret</password>")
|
149
221
|
end
|
222
|
+
|
223
|
+
it "qualifies elements embedded in complex types" do
|
224
|
+
client = new_client(:endpoint => @server.url(:repeat),
|
225
|
+
:wsdl => Fixture.wsdl(:elements_in_types))
|
226
|
+
msg = {":TopLevelTransaction"=>{":Qualified"=>"A Value"}}
|
227
|
+
|
228
|
+
response = client.call(:top_level_transaction, :message => msg)
|
229
|
+
|
230
|
+
expect(response.http.body.scan(/<tns:Qualified>/).count).to eq(1)
|
231
|
+
end
|
232
|
+
|
150
233
|
end
|
151
234
|
|
152
235
|
context "global :env_namespace" do
|
@@ -262,6 +345,15 @@ describe "Options" do
|
|
262
345
|
|
263
346
|
expect(logger).to eq(custom_logger)
|
264
347
|
end
|
348
|
+
|
349
|
+
it "sets the logger of HTTPI as well" do
|
350
|
+
custom_logger = Logger.new($stdout)
|
351
|
+
|
352
|
+
client = new_client(:logger => custom_logger, :log => true)
|
353
|
+
|
354
|
+
expect(HTTPI.logger).to be custom_logger
|
355
|
+
end
|
356
|
+
|
265
357
|
end
|
266
358
|
|
267
359
|
context "global :log_level" do
|
@@ -308,18 +400,27 @@ describe "Options" do
|
|
308
400
|
|
309
401
|
context "global :ssl_version" do
|
310
402
|
it "sets the SSL version to use" do
|
311
|
-
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:
|
403
|
+
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:TLSv1).twice
|
312
404
|
|
313
|
-
client = new_client(:endpoint => @server.url, :ssl_version => :
|
405
|
+
client = new_client(:endpoint => @server.url, :ssl_version => :TLSv1)
|
314
406
|
client.call(:authenticate)
|
315
407
|
end
|
316
408
|
end
|
317
409
|
|
318
410
|
context "global :ssl_verify_mode" do
|
319
411
|
it "sets the verify mode to use" do
|
320
|
-
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:
|
412
|
+
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:peer).twice
|
321
413
|
|
322
|
-
client = new_client(:endpoint => @server.url, :ssl_verify_mode => :
|
414
|
+
client = new_client(:endpoint => @server.url, :ssl_verify_mode => :peer)
|
415
|
+
client.call(:authenticate)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
context "global :ssl_ciphers" do
|
420
|
+
it "sets the ciphers to use" do
|
421
|
+
HTTPI::Auth::SSL.any_instance.expects(:ciphers=).with(:none).twice
|
422
|
+
|
423
|
+
client = new_client(:endpoint => @server.url, :ssl_ciphers => :none)
|
323
424
|
client.call(:authenticate)
|
324
425
|
end
|
325
426
|
end
|
@@ -334,6 +435,17 @@ describe "Options" do
|
|
334
435
|
end
|
335
436
|
end
|
336
437
|
|
438
|
+
context "global :ssl_cert_key" do
|
439
|
+
it "sets the cert key to use" do
|
440
|
+
cert_key = File.open(File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)).read
|
441
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_key=).with(cert_key).twice
|
442
|
+
|
443
|
+
client = new_client(:endpoint => @server.url, :ssl_cert_key => cert_key)
|
444
|
+
client.call(:authenticate)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
|
337
449
|
context "global :ssl_cert_key_password" do
|
338
450
|
it "sets the encrypted cert key file password to use" do
|
339
451
|
cert_key = File.expand_path("../../fixtures/ssl/client_encrypted_key.pem", __FILE__)
|
@@ -357,6 +469,16 @@ describe "Options" do
|
|
357
469
|
end
|
358
470
|
end
|
359
471
|
|
472
|
+
context "global :ssl_cert" do
|
473
|
+
it "sets the cert to use" do
|
474
|
+
cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
|
475
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert=).with(cert).twice
|
476
|
+
|
477
|
+
client = new_client(:endpoint => @server.url, :ssl_cert => cert)
|
478
|
+
client.call(:authenticate)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
360
482
|
context "global :ssl_ca_cert_file" do
|
361
483
|
it "sets the ca cert file to use" do
|
362
484
|
ca_cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
|
@@ -367,6 +489,37 @@ describe "Options" do
|
|
367
489
|
end
|
368
490
|
end
|
369
491
|
|
492
|
+
context "global :ssl_ca_cert_path" do
|
493
|
+
it "sets the ca cert path to use" do
|
494
|
+
ca_cert_path = "../../fixtures/ssl"
|
495
|
+
HTTPI::Auth::SSL.any_instance.expects(:ca_cert_path=).with(ca_cert_path).twice
|
496
|
+
|
497
|
+
client = new_client(:endpoint => @server.url, :ssl_ca_cert_path => ca_cert_path)
|
498
|
+
client.call(:authenticate)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "global :ssl_ca_cert_store" do
|
503
|
+
it "sets the cert store to use" do
|
504
|
+
cert_store = OpenSSL::X509::Store.new
|
505
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_store=).with(cert_store).twice
|
506
|
+
|
507
|
+
client = new_client(:endpoint => @server.url, :ssl_cert_store => cert_store)
|
508
|
+
client.call(:authenticate)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
context "global :ssl_ca_cert" do
|
513
|
+
it "sets the ca cert file to use" do
|
514
|
+
ca_cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
|
515
|
+
HTTPI::Auth::SSL.any_instance.expects(:ca_cert=).with(ca_cert).twice
|
516
|
+
|
517
|
+
client = new_client(:endpoint => @server.url, :ssl_ca_cert => ca_cert)
|
518
|
+
client.call(:authenticate)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
|
370
523
|
context "global :basic_auth" do
|
371
524
|
it "sets the basic auth credentials" do
|
372
525
|
client = new_client(:endpoint => @server.url(:basic_auth), :basic_auth => ["admin", "secret"])
|
@@ -385,133 +538,278 @@ describe "Options" do
|
|
385
538
|
end
|
386
539
|
end
|
387
540
|
|
541
|
+
context "global :ntlm" do
|
542
|
+
it "sets the ntlm credentials to use" do
|
543
|
+
credentials = ["admin", "secret"]
|
544
|
+
client = new_client(:endpoint => @server.url, :ntlm => credentials)
|
545
|
+
|
546
|
+
# TODO: find a way to integration test this. including an entire ntlm
|
547
|
+
# server implementation seems a bit over the top though.
|
548
|
+
HTTPI::Auth::Config.any_instance.expects(:ntlm).with(*credentials)
|
549
|
+
|
550
|
+
response = client.call(:authenticate)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
388
554
|
context "global :filters" do
|
389
555
|
it "filters a list of XML tags from logged SOAP messages" do
|
390
|
-
|
556
|
+
captured = mock_stdout do
|
391
557
|
client = new_client(:endpoint => @server.url(:repeat), :log => true)
|
392
|
-
|
393
558
|
client.globals[:filters] << :password
|
394
559
|
|
395
|
-
# filter out logs we're not interested in
|
396
|
-
client.globals[:logger].expects(:info).at_least_once
|
397
|
-
|
398
|
-
# check whether the password is filtered
|
399
|
-
client.globals[:logger].expects(:debug).with { |message|
|
400
|
-
message.include? "<password>***FILTERED***</password>"
|
401
|
-
}.twice
|
402
|
-
|
403
560
|
message = { :username => "luke", :password => "secret" }
|
404
561
|
client.call(:authenticate, :message => message)
|
405
562
|
end
|
563
|
+
|
564
|
+
captured.rewind
|
565
|
+
messages = captured.readlines.join("\n")
|
566
|
+
|
567
|
+
expect(messages).to include("<password>***FILTERED***</password>")
|
406
568
|
end
|
407
569
|
end
|
408
570
|
|
409
571
|
context "global :pretty_print_xml" do
|
410
572
|
it "is a nice but expensive way to debug XML messages" do
|
411
|
-
|
412
|
-
client = new_client(
|
573
|
+
captured = mock_stdout do
|
574
|
+
client = new_client(
|
575
|
+
:endpoint => @server.url(:repeat),
|
576
|
+
:pretty_print_xml => true,
|
577
|
+
:log => true)
|
578
|
+
client.globals[:logger].formatter = proc { |*, msg| "#{msg}\n" }
|
413
579
|
|
414
|
-
|
415
|
-
|
580
|
+
client.call(:authenticate)
|
581
|
+
end
|
416
582
|
|
417
|
-
|
418
|
-
|
419
|
-
envelope = message =~ /\n<env:Envelope/
|
420
|
-
body = message =~ /\n <env:Body>/
|
421
|
-
message_tag = message =~ /\n <tns:authenticate\/>/
|
583
|
+
captured.rewind
|
584
|
+
messages = captured.readlines.join("\n")
|
422
585
|
|
423
|
-
|
424
|
-
|
586
|
+
expect(messages).to match(/\n<env:Envelope/)
|
587
|
+
expect(messages).to match(/\n <env:Body/)
|
588
|
+
expect(messages).to match(/\n <tns:authenticate/)
|
589
|
+
end
|
590
|
+
end
|
425
591
|
|
426
|
-
|
592
|
+
context ":wsse_auth" do
|
593
|
+
let(:username) { "luke" }
|
594
|
+
let(:password) { "secret" }
|
595
|
+
let(:request) { response.http.body }
|
596
|
+
|
597
|
+
shared_examples "WSSE basic auth" do
|
598
|
+
it "adds WSSE basic auth information to the request" do
|
599
|
+
# the header and wsse security node
|
600
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
601
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
602
|
+
|
603
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
604
|
+
expect(request).to include("<wsse:UsernameToken")
|
605
|
+
expect(request).to include("wsu:Id=\"UsernameToken-1\"")
|
606
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
607
|
+
|
608
|
+
# the username and password node with type attribute
|
609
|
+
expect(request).to include("<wsse:Username>#{username}</wsse:Username>")
|
610
|
+
password_text = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
611
|
+
expect(request).to include("<wsse:Password Type=\"#{password_text}\">#{password}</wsse:Password>")
|
427
612
|
end
|
428
613
|
end
|
429
|
-
end
|
430
614
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
615
|
+
shared_examples "WSSE digest auth" do
|
616
|
+
it "adds WSSE digest auth information to the request" do
|
617
|
+
# the header and wsse security node
|
618
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
619
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
435
620
|
|
436
|
-
|
621
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
622
|
+
expect(request).to include("<wsse:UsernameToken")
|
623
|
+
expect(request).to include("wsu:Id=\"UsernameToken-1\"")
|
624
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
437
625
|
|
438
|
-
|
439
|
-
|
440
|
-
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
626
|
+
# the username node
|
627
|
+
expect(request).to include("<wsse:Username>#{username}</wsse:Username>")
|
441
628
|
|
442
|
-
|
443
|
-
|
444
|
-
expect(request).to include("wsu:Id=\"UsernameToken-1\"")
|
445
|
-
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
629
|
+
# the nonce node
|
630
|
+
expect(request).to match(/<wsse:Nonce.*>.+\n?<\/wsse:Nonce>/)
|
446
631
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
632
|
+
# the created node with a timestamp
|
633
|
+
expect(request).to match(/<wsu:Created>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Created>/)
|
634
|
+
|
635
|
+
# the password node contains the encrypted value
|
636
|
+
password_digest = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
637
|
+
expect(request).to match(/<wsse:Password Type=\"#{password_digest}\">.+<\/wsse:Password>/)
|
638
|
+
expect(request).to_not include(password)
|
639
|
+
end
|
451
640
|
end
|
452
641
|
|
453
|
-
|
454
|
-
|
455
|
-
|
642
|
+
shared_examples "no WSSE auth" do
|
643
|
+
it "does not add WSSE auth to the request" do
|
644
|
+
expect(request).not_to include("<wsse:UsernameToken")
|
645
|
+
end
|
646
|
+
end
|
456
647
|
|
457
|
-
|
648
|
+
describe "global" do
|
649
|
+
context "enabled" do
|
650
|
+
context "without digest" do
|
651
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password]) }
|
652
|
+
let(:response) { client.call(:authenticate) }
|
653
|
+
include_examples "WSSE basic auth"
|
654
|
+
end
|
655
|
+
|
656
|
+
context "with digest" do
|
657
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password, :digest]) }
|
658
|
+
let(:response) { client.call(:authenticate) }
|
659
|
+
include_examples "WSSE digest auth"
|
660
|
+
end
|
458
661
|
|
459
|
-
|
460
|
-
|
461
|
-
|
662
|
+
context "local override" do
|
663
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => ["luke", "secret"]) }
|
664
|
+
|
665
|
+
context "enabled" do
|
666
|
+
let(:username) { "lea" }
|
667
|
+
let(:password) { "top-secret" }
|
668
|
+
|
669
|
+
context "without digest" do
|
670
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password)} }
|
671
|
+
include_examples "WSSE basic auth"
|
672
|
+
end
|
673
|
+
|
674
|
+
context "with digest" do
|
675
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password, :digest)} }
|
676
|
+
include_examples "WSSE digest auth"
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
context "disabled" do
|
681
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(false)} }
|
682
|
+
include_examples "no WSSE auth"
|
683
|
+
end
|
684
|
+
|
685
|
+
context "set to nil" do
|
686
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(nil)} }
|
687
|
+
include_examples "WSSE basic auth"
|
688
|
+
end
|
689
|
+
end
|
462
690
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
691
|
+
context "global" do
|
692
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_auth => [username, password, :digest]) }
|
693
|
+
let(:response) { client.call(:authenticate) }
|
694
|
+
include_examples "WSSE digest auth"
|
695
|
+
end
|
696
|
+
end
|
467
697
|
|
468
|
-
|
469
|
-
|
698
|
+
context "not enabled" do
|
699
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat)) }
|
470
700
|
|
471
|
-
|
472
|
-
|
701
|
+
describe "local" do
|
702
|
+
context "enabled" do
|
703
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_auth(username, password, :digest)} }
|
704
|
+
include_examples "WSSE digest auth"
|
705
|
+
end
|
473
706
|
|
474
|
-
|
475
|
-
|
707
|
+
context "disabled" do
|
708
|
+
let(:response) { client.call(:authenticate) { |locals| locals.wsse_auth(false)} }
|
709
|
+
include_examples "no WSSE auth"
|
710
|
+
end
|
476
711
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
712
|
+
context "set to nil" do
|
713
|
+
let(:response) { client.call(:authenticate) { |locals| locals.wsse_auth(nil)} }
|
714
|
+
include_examples "no WSSE auth"
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
481
718
|
end
|
482
719
|
end
|
483
720
|
|
484
|
-
context "
|
485
|
-
|
486
|
-
client = new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true)
|
487
|
-
response = client.call(:authenticate)
|
721
|
+
context ":wsse_timestamp" do
|
722
|
+
let(:request) { response.http.body }
|
488
723
|
|
489
|
-
|
724
|
+
shared_examples "WSSE timestamp" do
|
725
|
+
it "adds WSSE timestamp auth information to the request" do
|
726
|
+
# the header and wsse security node
|
727
|
+
wsse_namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
728
|
+
expect(request).to include("<env:Header><wsse:Security xmlns:wsse=\"#{wsse_namespace}\">")
|
490
729
|
|
491
|
-
|
492
|
-
|
493
|
-
|
730
|
+
# split up to prevent problems with unordered Hash attributes in 1.8 [dh, 2012-12-13]
|
731
|
+
expect(request).to include("<wsu:Timestamp")
|
732
|
+
expect(request).to include("wsu:Id=\"Timestamp-1\"")
|
733
|
+
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
494
734
|
|
495
|
-
|
496
|
-
|
497
|
-
expect(request).to include("wsu:Id=\"Timestamp-1\"")
|
498
|
-
expect(request).to include("xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"")
|
735
|
+
# the created node with a timestamp
|
736
|
+
expect(request).to match(/<wsu:Created>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Created>/)
|
499
737
|
|
500
|
-
|
501
|
-
|
738
|
+
# the expires node with a timestamp
|
739
|
+
expect(request).to match(/<wsu:Expires>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Expires>/)
|
740
|
+
end
|
741
|
+
end
|
502
742
|
|
503
|
-
|
504
|
-
|
743
|
+
shared_examples "no WSSE timestamp" do
|
744
|
+
it "does not add WSSE timestamp to the request" do
|
745
|
+
expect(request).not_to include("<wsu:Timestamp")
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
describe "global" do
|
750
|
+
context "enabled" do
|
751
|
+
context "through block without arguments" do
|
752
|
+
let(:client) do
|
753
|
+
new_client(:endpoint => @server.url(:repeat)) do |globals|
|
754
|
+
globals.wsse_timestamp
|
755
|
+
end
|
756
|
+
end
|
757
|
+
let(:response) { client.call(:authenticate) }
|
758
|
+
include_examples "WSSE timestamp"
|
759
|
+
end
|
760
|
+
|
761
|
+
context "through initializer options" do
|
762
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true) }
|
763
|
+
let(:response) { client.call(:authenticate) }
|
764
|
+
include_examples "WSSE timestamp"
|
765
|
+
end
|
766
|
+
|
767
|
+
context "with local override" do
|
768
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat), :wsse_timestamp => true) }
|
769
|
+
context "enabled" do
|
770
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp} }
|
771
|
+
include_examples "WSSE timestamp"
|
772
|
+
end
|
773
|
+
context "disabled" do
|
774
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(false) } }
|
775
|
+
include_examples "no WSSE timestamp"
|
776
|
+
end
|
777
|
+
context "set to nil" do
|
778
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(nil) } }
|
779
|
+
include_examples "WSSE timestamp"
|
780
|
+
end
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
context "not enabled" do
|
785
|
+
let(:client) { new_client(:endpoint => @server.url(:repeat)) }
|
786
|
+
describe "local" do
|
787
|
+
context "enabled" do
|
788
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp} }
|
789
|
+
include_examples "WSSE timestamp"
|
790
|
+
end
|
791
|
+
context "disabled" do
|
792
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(false) } }
|
793
|
+
include_examples "no WSSE timestamp"
|
794
|
+
end
|
795
|
+
context "set to nil" do
|
796
|
+
let(:response) { client.call(:authenticate) {|locals| locals.wsse_timestamp(nil) } }
|
797
|
+
include_examples "no WSSE timestamp"
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
505
801
|
end
|
506
802
|
end
|
507
803
|
|
508
804
|
context "global :strip_namespaces" do
|
509
805
|
it "can be changed to not strip any namespaces" do
|
510
|
-
client = new_client(
|
511
|
-
|
806
|
+
client = new_client(
|
807
|
+
:endpoint => @server.url(:repeat),
|
808
|
+
:convert_response_tags_to => lambda { |tag| tag.snakecase },
|
809
|
+
:strip_namespaces => false
|
810
|
+
)
|
512
811
|
|
513
|
-
|
514
|
-
expect { response.body }.to raise_error(Savon::InvalidResponseError)
|
812
|
+
response = client.call(:authenticate, :xml => Fixture.response(:authentication))
|
515
813
|
|
516
814
|
expect(response.hash["soap:envelope"]["soap:body"]).to include("ns2:authenticate_response")
|
517
815
|
end
|
@@ -562,6 +860,125 @@ describe "Options" do
|
|
562
860
|
end
|
563
861
|
end
|
564
862
|
|
863
|
+
context "global :convert_attributes_to" do
|
864
|
+
it "changes how XML tag attributes from the SOAP response are translated into Hash keys" do
|
865
|
+
client = new_client(:endpoint => @server.url(:repeat), :convert_attributes_to => lambda {|k,v| [k,v]})
|
866
|
+
response = client.call(:authenticate, :xml => Fixture.response(:f5))
|
867
|
+
expect(response.body[:get_agent_listen_address_response][:return][:item].first[:ipport][:address]).to eq({:"@s:type"=>"y:string"})
|
868
|
+
end
|
869
|
+
|
870
|
+
it "strips the attributes if an appropriate lambda is set" do
|
871
|
+
client = new_client(:endpoint => @server.url(:repeat), :convert_attributes_to => lambda {|k,v| []})
|
872
|
+
response = client.call(:authenticate, :xml => Fixture.response(:f5))
|
873
|
+
expect(response.body[:get_agent_listen_address_response][:return][:item].first[:ipport][:address]).to eq(nil)
|
874
|
+
end
|
875
|
+
|
876
|
+
it "accepts a block in the block-based interface" do
|
877
|
+
client = Savon.client do |globals|
|
878
|
+
globals.log false
|
879
|
+
globals.wsdl Fixture.wsdl(:authentication)
|
880
|
+
globals.endpoint @server.url(:repeat)
|
881
|
+
globals.convert_attributes_to {|k,v| [k,v]}
|
882
|
+
end
|
883
|
+
|
884
|
+
response = client.call(:authenticate) do |locals|
|
885
|
+
locals.xml Fixture.response(:f5)
|
886
|
+
end
|
887
|
+
|
888
|
+
expect(response.body[:get_agent_listen_address_response][:return][:item].first[:ipport][:address]).to eq({:"@s:type"=>"y:string"})
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
context 'global: :adapter' do
|
893
|
+
it 'passes option to Wasabi initializer for WSDL fetching' do
|
894
|
+
## I want to use there something similar to the next mock expectation, but I can't
|
895
|
+
## as due to how Savon sets up Wasabi::Document and Wasabi::Document initialize itself
|
896
|
+
## adapter= method is called first time with nil and second time with adapter. [Envek, 2014-05-03]
|
897
|
+
# Wasabi::Document.any_instance.expects(:adapter=).with(:fake_adapter_for_test)
|
898
|
+
client = Savon.client(
|
899
|
+
:log => false,
|
900
|
+
:wsdl => @server.url(:authentication),
|
901
|
+
:adapter => :fake_adapter_for_test,
|
902
|
+
)
|
903
|
+
operations = client.operations
|
904
|
+
expect(operations).to eq([:authenticate])
|
905
|
+
expect(FakeAdapterForTest.class_variable_get(:@@requests).size).to eq(1)
|
906
|
+
expect(FakeAdapterForTest.class_variable_get(:@@requests).first.url).to eq(URI.parse(@server.url(:authentication)))
|
907
|
+
expect(FakeAdapterForTest.class_variable_get(:@@methods)).to eq([:get])
|
908
|
+
end
|
909
|
+
|
910
|
+
it 'instructs HTTPI to use provided adapter for performing SOAP requests' do
|
911
|
+
client = new_client_without_wsdl(
|
912
|
+
:endpoint => @server.url(:repeat),
|
913
|
+
:namespace => "http://v1.example.com",
|
914
|
+
:adapter => :adapter_for_test,
|
915
|
+
)
|
916
|
+
response = client.call(:authenticate)
|
917
|
+
expect(response.http.body).to include('xmlns:wsdl="http://v1.example.com"')
|
918
|
+
expect(response.http.body).to include('<wsdl:authenticate>')
|
919
|
+
expect(AdapterForTest.class_variable_get(:@@requests).size).to eq(1)
|
920
|
+
expect(AdapterForTest.class_variable_get(:@@requests).first.url).to eq(URI.parse(@server.url(:repeat)))
|
921
|
+
expect(AdapterForTest.class_variable_get(:@@methods)).to eq([:post])
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
context "global and request :soap_header" do
|
926
|
+
it "merges the headers if both were provided as Hashes" do
|
927
|
+
global_soap_header = {
|
928
|
+
:global_header => { :auth_token => "secret" },
|
929
|
+
:merged => { :global => true }
|
930
|
+
}
|
931
|
+
|
932
|
+
request_soap_header = {
|
933
|
+
:request_header => { :auth_token => "secret" },
|
934
|
+
:merged => { :request => true }
|
935
|
+
}
|
936
|
+
|
937
|
+
client = new_client(:endpoint => @server.url(:repeat), :soap_header => global_soap_header)
|
938
|
+
|
939
|
+
response = client.call(:authenticate, :soap_header => request_soap_header)
|
940
|
+
request_body = response.http.body
|
941
|
+
|
942
|
+
expect(request_body).to include("<globalHeader><authToken>secret</authToken></globalHeader>")
|
943
|
+
expect(request_body).to include("<requestHeader><authToken>secret</authToken></requestHeader>")
|
944
|
+
expect(request_body).to include("<merged><request>true</request></merged>")
|
945
|
+
end
|
946
|
+
|
947
|
+
it "prefers the request over the global option if at least one of them is not a Hash" do
|
948
|
+
global_soap_header = "<global>header</global>"
|
949
|
+
request_soap_header = "<request>header</request>"
|
950
|
+
|
951
|
+
client = new_client(:endpoint => @server.url(:repeat), :soap_header => global_soap_header)
|
952
|
+
|
953
|
+
response = client.call(:authenticate, :soap_header => request_soap_header)
|
954
|
+
request_body = response.http.body
|
955
|
+
|
956
|
+
expect(request_body).to include("<env:Header><request>header</request></env:Header>")
|
957
|
+
end
|
958
|
+
end
|
959
|
+
|
960
|
+
context "request :soap_header" do
|
961
|
+
it "accepts a Hash of SOAP header information" do
|
962
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
963
|
+
|
964
|
+
response = client.call(:authenticate, :soap_header => { :auth_token => "secret" })
|
965
|
+
expect(response.http.body).to include("<env:Header><authToken>secret</authToken></env:Header>")
|
966
|
+
end
|
967
|
+
|
968
|
+
it "accepts anything other than a String and calls #to_s on it" do
|
969
|
+
to_s_header = Class.new {
|
970
|
+
def to_s
|
971
|
+
"to_s_header"
|
972
|
+
end
|
973
|
+
}.new
|
974
|
+
|
975
|
+
client = new_client(:endpoint => @server.url(:repeat))
|
976
|
+
|
977
|
+
response = client.call(:authenticate, :soap_header => to_s_header)
|
978
|
+
expect(response.http.body).to include("<env:Header>to_s_header</env:Header>")
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
565
982
|
context "request: message_tag" do
|
566
983
|
it "when set, changes the SOAP message tag" do
|
567
984
|
response = new_client(:endpoint => @server.url(:repeat)).call(:authenticate, :message_tag => :doAuthenticate)
|
@@ -669,6 +1086,17 @@ describe "Options" do
|
|
669
1086
|
end
|
670
1087
|
end
|
671
1088
|
|
1089
|
+
context "request :headers" do
|
1090
|
+
it "sets headers" do
|
1091
|
+
client = new_client(:endpoint => @server.url(:inspect_request))
|
1092
|
+
|
1093
|
+
response = client.call(:authenticate, :headers => { "X-Token" => "secret" })
|
1094
|
+
x_token = inspect_request(response).x_token
|
1095
|
+
|
1096
|
+
expect(x_token).to eq("secret")
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
672
1100
|
def new_client(globals = {}, &block)
|
673
1101
|
globals = { :wsdl => Fixture.wsdl(:authentication), :log => false }.merge(globals)
|
674
1102
|
Savon.client(globals, &block)
|