savon 2.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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