savon 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 "/repeat_header" do
42
+ map "/inspect_request" do
42
43
  run lambda { |env|
43
- IntegrationServer.respond_with :body => env["HTTP_REPEAT_HEADER"]
44
- }
45
- end
46
-
47
- map "/inspect_header" do
48
- run lambda { |env|
49
- IntegrationServer.respond_with :body => env[env["HTTP_INSPECT"]]
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
 
@@ -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
- Savon::Operation.expects(:create).returns(operation)
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(:authentication) }
8
- let(:no_wsdl) { Wasabi::Document.new }
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 = Savon::Operation.create(:authenticate, wsdl, globals)
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 { Savon::Operation.create("not a symbol", wsdl, globals) }.
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 { Savon::Operation.create(:no_such_operation, wsdl, globals) }.
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 = Savon::Operation.create(:authenticate, no_wsdl, globals)
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 = Savon::Operation.create(:authenticate, wsdl, globals)
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
@@ -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(:repeat_header), :headers => { "Repeat-Header" => "savon" })
64
+ client = new_client(:endpoint => @server.url(:inspect_request), :headers => { "X-Token" => "secret" })
52
65
 
53
66
  response = client.call(:authenticate)
54
- expect(response.http.body).to eq("savon")
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
- # TODO: make HTTPI tag timeout errors, then depend on HTTPI::TimeoutError instead of a specific client error [dh, 2012-12-08]
64
- expect { client.call(:authenticate) }.to raise_error(HTTPClient::ConnectTimeoutError)
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) }.to raise_error(HTTPClient::ReceiveTimeoutError)
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(:inspect_header), :encoding => "ISO-8859-1",
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
- expect(response.http.body).to eq("text/xml;charset=ISO-8859-1")
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 "silences HTTPI" do
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(:inspect_header),
519
- :headers => { "Inspect" => "HTTP_SOAPACTION" })
593
+ client = new_client(:endpoint => @server.url(:inspect_request))
520
594
 
521
595
  response = client.call(:authenticate)
522
- expect(response.http.body).to eq('"authenticate"')
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(:inspect_header),
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
- expect(response.http.body).to eq('"doAuthenticate"')
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