savon 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +119 -104
- data/README.md +12 -11
- data/Rakefile +0 -6
- data/lib/savon.rb +16 -14
- data/lib/savon/block_interface.rb +26 -0
- data/lib/savon/builder.rb +142 -0
- data/lib/savon/client.rb +36 -135
- data/lib/savon/header.rb +42 -0
- data/lib/savon/http_error.rb +27 -0
- data/lib/savon/log_message.rb +23 -25
- data/lib/savon/message.rb +35 -0
- data/lib/savon/mock.rb +5 -0
- data/lib/savon/mock/expectation.rb +70 -0
- data/lib/savon/mock/spec_helper.rb +62 -0
- data/lib/savon/model.rb +39 -61
- data/lib/savon/operation.rb +62 -0
- data/lib/savon/options.rb +265 -0
- data/lib/savon/qualified_message.rb +49 -0
- data/lib/savon/request.rb +92 -0
- data/lib/savon/response.rb +97 -0
- data/lib/savon/soap_fault.rb +40 -0
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +10 -8
- data/spec/integration/options_spec.rb +536 -0
- data/spec/integration/request_spec.rb +31 -16
- data/spec/integration/support/application.rb +80 -0
- data/spec/integration/support/server.rb +84 -0
- data/spec/savon/builder_spec.rb +81 -0
- data/spec/savon/client_spec.rb +90 -488
- data/spec/savon/http_error_spec.rb +49 -0
- data/spec/savon/log_message_spec.rb +33 -0
- data/spec/savon/mock_spec.rb +127 -0
- data/spec/savon/model_spec.rb +110 -99
- data/spec/savon/observers_spec.rb +92 -0
- data/spec/savon/operation_spec.rb +49 -0
- data/spec/savon/request_spec.rb +145 -0
- data/spec/savon/{soap/response_spec.rb → response_spec.rb} +22 -59
- data/spec/savon/soap_fault_spec.rb +94 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/support/fixture.rb +5 -1
- metadata +202 -197
- data/lib/savon/config.rb +0 -46
- data/lib/savon/error.rb +0 -6
- data/lib/savon/hooks/group.rb +0 -68
- data/lib/savon/hooks/hook.rb +0 -61
- data/lib/savon/http/error.rb +0 -42
- data/lib/savon/logger.rb +0 -39
- data/lib/savon/null_logger.rb +0 -10
- data/lib/savon/soap.rb +0 -21
- data/lib/savon/soap/fault.rb +0 -59
- data/lib/savon/soap/invalid_response_error.rb +0 -13
- data/lib/savon/soap/request.rb +0 -86
- data/lib/savon/soap/request_builder.rb +0 -205
- data/lib/savon/soap/response.rb +0 -117
- data/lib/savon/soap/xml.rb +0 -257
- data/spec/savon/config_spec.rb +0 -38
- data/spec/savon/hooks/group_spec.rb +0 -71
- data/spec/savon/hooks/hook_spec.rb +0 -16
- data/spec/savon/http/error_spec.rb +0 -52
- data/spec/savon/logger_spec.rb +0 -51
- data/spec/savon/savon_spec.rb +0 -33
- data/spec/savon/soap/fault_spec.rb +0 -89
- data/spec/savon/soap/request_builder_spec.rb +0 -207
- data/spec/savon/soap/request_spec.rb +0 -112
- data/spec/savon/soap/xml_spec.rb +0 -357
- data/spec/savon/soap_spec.rb +0 -16
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Savon::HTTPError do
|
4
|
+
let(:http_error) { Savon::HTTPError.new new_response(:code => 404, :body => "Not Found") }
|
5
|
+
let(:no_error) { Savon::HTTPError.new new_response }
|
6
|
+
|
7
|
+
it "inherits from Savon::Error" do
|
8
|
+
expect(Savon::HTTPError.ancestors).to include(Savon::Error)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".present?" do
|
12
|
+
it "returns true if there was an HTTP error" do
|
13
|
+
http = new_response(:code => 404, :body => "Not Found")
|
14
|
+
expect(Savon::HTTPError.present? http).to be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns false unless there was an HTTP error" do
|
18
|
+
expect(Savon::HTTPError.present? new_response).to be_false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#http" do
|
23
|
+
it "returns the HTTPI::Response" do
|
24
|
+
expect(http_error.http).to be_a(HTTPI::Response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[:message, :to_s].each do |method|
|
29
|
+
describe "##{method}" do
|
30
|
+
it "returns the HTTP error message" do
|
31
|
+
expect(http_error.send method).to eq("HTTP error (404): Not Found")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#to_hash" do
|
37
|
+
it "returns the HTTP response details as a Hash" do
|
38
|
+
expect(http_error.to_hash).to eq(:code => 404, :headers => {}, :body => "Not Found")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def new_response(options = {})
|
43
|
+
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
|
44
|
+
response = defaults.merge options
|
45
|
+
|
46
|
+
HTTPI::Response.new response[:code], response[:headers], response[:body]
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Savon::LogMessage do
|
4
|
+
|
5
|
+
it "returns the message if it's not XML" do
|
6
|
+
message = log_message("hello", [:password], :pretty_print).to_s
|
7
|
+
expect(message).to eq("hello")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns the message if it shouldn't be filtered or pretty printed" do
|
11
|
+
Nokogiri.expects(:XML).never
|
12
|
+
|
13
|
+
message = log_message("<hello/>", [], false).to_s
|
14
|
+
expect(message).to eq("<hello/>")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "pretty prints a given message" do
|
18
|
+
message = log_message("<envelope><body>hello</body></envelope>", [], :pretty_print).to_s
|
19
|
+
|
20
|
+
expect(message).to include("\n<envelope>")
|
21
|
+
expect(message).to include("\n <body>")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "filters tags in a given message" do
|
25
|
+
message = log_message("<root><password>secret</password></root>", [:password], false).to_s
|
26
|
+
expect(message).to include("<password>***FILTERED***</password>")
|
27
|
+
end
|
28
|
+
|
29
|
+
def log_message(*args)
|
30
|
+
Savon::LogMessage.new(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "savon/mock/spec_helper"
|
3
|
+
|
4
|
+
describe "Savon's mock interface" do
|
5
|
+
include Savon::SpecHelper
|
6
|
+
|
7
|
+
before :all do
|
8
|
+
savon.mock!
|
9
|
+
end
|
10
|
+
|
11
|
+
after :all do
|
12
|
+
savon.unmock!
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can verify a request and return a fixture response" do
|
16
|
+
message = { :username => "luke", :password => "secret" }
|
17
|
+
savon.expects(:authenticate).with(:message => message).returns("<fixture/>")
|
18
|
+
|
19
|
+
response = new_client.call(:authenticate) do
|
20
|
+
message(:username => "luke", :password => "secret")
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(response.http.body).to eq("<fixture/>")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "accepts a Hash to specify the response code, headers and body" do
|
27
|
+
soap_fault = Fixture.response(:soap_fault)
|
28
|
+
response = { :code => 500, :headers => { "X-Result" => "invalid" }, :body => soap_fault }
|
29
|
+
|
30
|
+
savon.expects(:authenticate).returns(response)
|
31
|
+
response = new_client(:raise_errors => false).call(:authenticate)
|
32
|
+
|
33
|
+
expect(response).to_not be_successful
|
34
|
+
expect(response).to be_a_soap_fault
|
35
|
+
|
36
|
+
expect(response.http.code).to eq(500)
|
37
|
+
expect(response.http.headers).to eq("X-Result" => "invalid")
|
38
|
+
expect(response.http.body).to eq(soap_fault)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "works with multiple requests" do
|
42
|
+
authentication_message = { :username => "luke", :password => "secret" }
|
43
|
+
savon.expects(:authenticate).with(:message => authentication_message).returns("")
|
44
|
+
|
45
|
+
find_user_message = { :by_username => "lea" }
|
46
|
+
savon.expects(:find_user).with(:message => find_user_message).returns("")
|
47
|
+
|
48
|
+
new_client.call(:authenticate, :message => authentication_message)
|
49
|
+
new_client.call(:find_user, :message => find_user_message)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "fails when the expected operation was not called" do
|
53
|
+
# TODO: find out how to test this! [dh, 2012-12-17]
|
54
|
+
#savon.expects(:authenticate)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "fails when the return value for an expectation was not specified" do
|
58
|
+
savon.expects(:authenticate)
|
59
|
+
|
60
|
+
expect { new_client.call(:authenticate) }.
|
61
|
+
to raise_error(Savon::ExpectationError, "This expectation was not set up with a response.")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "fails with an unexpected request" do
|
65
|
+
expect { new_client.call(:authenticate) }.
|
66
|
+
to raise_error(Savon::ExpectationError, "Unexpected request to the :authenticate operation.")
|
67
|
+
end
|
68
|
+
|
69
|
+
it "fails with multiple requests" do
|
70
|
+
authentication_message = { :username => "luke", :password => "secret" }
|
71
|
+
savon.expects(:authenticate).with(:message => authentication_message).returns("")
|
72
|
+
|
73
|
+
create_user_message = { :username => "lea" }
|
74
|
+
savon.expects(:create_user).with(:message => create_user_message).returns("")
|
75
|
+
|
76
|
+
find_user_message = { :by_username => "lea" }
|
77
|
+
savon.expects(:find_user).with(:message => find_user_message).returns("")
|
78
|
+
|
79
|
+
# reversed order from previous spec
|
80
|
+
new_client.call(:authenticate, :message => authentication_message)
|
81
|
+
|
82
|
+
expect { new_client.call(:find_user, :message => find_user_message) }.
|
83
|
+
to raise_error(Savon::ExpectationError, "Expected a request to the :create_user operation.\n" \
|
84
|
+
"Received a request to the :find_user operation instead.")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "fails when the expected SOAP operation does not match the actual one" do
|
88
|
+
savon.expects(:logout).returns("<fixture/>")
|
89
|
+
|
90
|
+
expect { new_client.call(:authenticate) }.
|
91
|
+
to raise_error(Savon::ExpectationError, "Expected a request to the :logout operation.\n" \
|
92
|
+
"Received a request to the :authenticate operation instead.")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "fails when there is no actual message to match" do
|
96
|
+
message = { :username => "luke" }
|
97
|
+
savon.expects(:find_user).with(:message => message).returns("<fixture/>")
|
98
|
+
|
99
|
+
expect { new_client.call(:find_user) }.
|
100
|
+
to raise_error(Savon::ExpectationError, "Expected a request to the :find_user operation\n" \
|
101
|
+
" with this message: #{message.inspect}\n" \
|
102
|
+
"Received a request to the :find_user operation\n" \
|
103
|
+
" with no message.")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "fails when there is no expect but an actual message" do
|
107
|
+
savon.expects(:find_user).returns("<fixture/>")
|
108
|
+
message = { :username => "luke" }
|
109
|
+
|
110
|
+
expect { new_client.call(:find_user, :message => message) }.
|
111
|
+
to raise_error(Savon::ExpectationError, "Expected a request to the :find_user operation\n" \
|
112
|
+
" with no message.\n" \
|
113
|
+
"Received a request to the :find_user operation\n" \
|
114
|
+
" with this message: #{message.inspect}")
|
115
|
+
end
|
116
|
+
|
117
|
+
def new_client(globals = {})
|
118
|
+
defaults = {
|
119
|
+
:endpoint => "http://example.com",
|
120
|
+
:namespace => "http://v1.example.com",
|
121
|
+
:log => false
|
122
|
+
}
|
123
|
+
|
124
|
+
Savon.client defaults.merge(globals)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
data/spec/savon/model_spec.rb
CHANGED
@@ -1,144 +1,155 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "integration/support/server"
|
2
3
|
|
3
4
|
describe Savon::Model do
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
before :all do
|
7
|
+
@server = IntegrationServer.run
|
7
8
|
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
model.client.should equal(model.client)
|
12
|
-
end
|
10
|
+
after :all do
|
11
|
+
@server.stop
|
13
12
|
end
|
14
13
|
|
15
|
-
describe ".
|
16
|
-
it "
|
17
|
-
model.
|
18
|
-
|
14
|
+
describe ".client" do
|
15
|
+
it "returns the memoized client" do
|
16
|
+
model = Class.new {
|
17
|
+
extend Savon::Model
|
18
|
+
client :wsdl => Fixture.wsdl(:authentication)
|
19
|
+
}
|
20
|
+
|
21
|
+
expect(model.client).to be_a(Savon::Client)
|
22
|
+
expect(model.client).to equal(model.client)
|
19
23
|
end
|
20
|
-
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
model.namespace "http://v1.example.com"
|
25
|
-
model.client.wsdl.namespace.should == "http://v1.example.com"
|
26
|
-
end
|
27
|
-
end
|
25
|
+
it "raises if the client was not initialized properly" do
|
26
|
+
model = Class.new { extend Savon::Model }
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
model.document "http://example.com/?wsdl"
|
32
|
-
model.client.wsdl.document.should == "http://example.com/?wsdl"
|
28
|
+
expect { model.client }.
|
29
|
+
to raise_error(Savon::InitializationError, /^Expected the model to be initialized/)
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
36
|
-
describe ".
|
37
|
-
it "sets
|
38
|
-
model.
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
33
|
+
describe ".global" do
|
34
|
+
it "sets global options" do
|
35
|
+
model = Class.new {
|
36
|
+
extend Savon::Model
|
42
37
|
|
43
|
-
|
44
|
-
it "sets HTTP Basic auth credentials" do
|
45
|
-
model.basic_auth "login", "password"
|
46
|
-
model.client.http.auth.basic.should == ["login", "password"]
|
47
|
-
end
|
48
|
-
end
|
38
|
+
client :wsdl => Fixture.wsdl(:authentication)
|
49
39
|
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
global :soap_version, 2
|
41
|
+
global :open_timeout, 71
|
42
|
+
global :wsse_auth, "luke", "secret", :digest
|
43
|
+
}
|
53
44
|
|
54
|
-
model.client.
|
55
|
-
model.client.
|
56
|
-
model.client.
|
45
|
+
expect(model.client.globals[:soap_version]).to eq(2)
|
46
|
+
expect(model.client.globals[:open_timeout]).to eq(71)
|
47
|
+
expect(model.client.globals[:wsse_auth]).to eq(["luke", "secret", :digest])
|
57
48
|
end
|
58
49
|
end
|
59
50
|
|
60
|
-
describe ".
|
61
|
-
|
62
|
-
|
63
|
-
|
51
|
+
describe ".operations" do
|
52
|
+
subject(:model)
|
53
|
+
it "defines class methods for each operation" do
|
54
|
+
model = Class.new {
|
55
|
+
extend Savon::Model
|
64
56
|
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
client :wsdl => Fixture.wsdl(:authentication)
|
58
|
+
operations :authenticate
|
59
|
+
}
|
68
60
|
|
69
|
-
|
70
|
-
model.new.should respond_to(:get_user, :get_all_users)
|
61
|
+
expect(model).to respond_to(:authenticate)
|
71
62
|
end
|
72
63
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
64
|
+
it "executes class-level SOAP operations" do
|
65
|
+
repeat_url = @server.url(:repeat)
|
66
|
+
|
67
|
+
model = Class.new {
|
68
|
+
extend Savon::Model
|
69
|
+
|
70
|
+
client :endpoint => repeat_url, :namespace => "http://v1.example.com"
|
71
|
+
global :log, false
|
78
72
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
73
|
+
operations :authenticate
|
74
|
+
}
|
75
|
+
|
76
|
+
response = model.authenticate(:xml => Fixture.response(:authentication))
|
77
|
+
expect(response.body[:authenticate_response][:return]).to include(:authentication_value)
|
83
78
|
end
|
84
79
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
80
|
+
it "defines instance methods for each operation" do
|
81
|
+
model = Class.new {
|
82
|
+
extend Savon::Model
|
83
|
+
|
84
|
+
client :wsdl => Fixture.wsdl(:authentication)
|
85
|
+
operations :authenticate
|
86
|
+
}
|
87
|
+
|
88
|
+
model_instance = model.new
|
89
|
+
expect(model_instance).to respond_to(:authenticate)
|
90
90
|
end
|
91
|
-
end
|
92
91
|
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
it "executes instance-level SOAP operations" do
|
93
|
+
repeat_url = @server.url(:repeat)
|
94
|
+
|
95
|
+
model = Class.new {
|
96
|
+
extend Savon::Model
|
97
|
+
|
98
|
+
client :endpoint => repeat_url, :namespace => "http://v1.example.com"
|
99
|
+
global :log, false
|
100
|
+
|
101
|
+
operations :authenticate
|
102
|
+
}
|
103
|
+
|
104
|
+
model_instance = model.new
|
105
|
+
response = model_instance.authenticate(:xml => Fixture.response(:authentication))
|
106
|
+
expect(response.body[:authenticate_response][:return]).to include(:authentication_value)
|
96
107
|
end
|
97
108
|
end
|
98
109
|
|
99
|
-
|
100
|
-
|
101
|
-
let(:supermodel) do
|
102
|
-
supermodel = model.dup
|
103
|
-
supermodel.actions :get_user
|
104
|
-
|
105
|
-
def supermodel.get_user(body = nil, &block)
|
106
|
-
p "super"
|
107
|
-
super
|
108
|
-
end
|
110
|
+
it "allows to overwrite class operations" do
|
111
|
+
repeat_url = @server.url(:repeat)
|
109
112
|
|
110
|
-
|
111
|
-
|
113
|
+
model = Class.new {
|
114
|
+
extend Savon::Model
|
115
|
+
client :endpoint => repeat_url, :namespace => "http://v1.example.com"
|
116
|
+
}
|
112
117
|
|
113
|
-
|
114
|
-
|
115
|
-
supermodel.expects(:p).with("super") # stupid, but works
|
118
|
+
supermodel = model.dup
|
119
|
+
supermodel.operations :authenticate
|
116
120
|
|
117
|
-
|
118
|
-
|
121
|
+
def supermodel.authenticate(locals = {})
|
122
|
+
p "super"
|
123
|
+
super
|
119
124
|
end
|
120
125
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
+
supermodel.client.expects(:call).with(:authenticate, :message => { :username => "luke", :password => "secret" })
|
127
|
+
supermodel.expects(:p).with("super") # stupid, but works
|
128
|
+
|
129
|
+
supermodel.authenticate(:message => { :username => "luke", :password => "secret" })
|
130
|
+
end
|
126
131
|
|
127
|
-
|
128
|
-
|
129
|
-
super
|
130
|
-
end
|
132
|
+
it "allows to overwrite instance operations" do
|
133
|
+
repeat_url = @server.url(:repeat)
|
131
134
|
|
132
|
-
|
133
|
-
|
135
|
+
model = Class.new {
|
136
|
+
extend Savon::Model
|
137
|
+
client :endpoint => repeat_url, :namespace => "http://v1.example.com"
|
138
|
+
}
|
134
139
|
|
135
|
-
|
136
|
-
|
137
|
-
|
140
|
+
supermodel = model.dup
|
141
|
+
supermodel.operations :authenticate
|
142
|
+
supermodel = supermodel.new
|
138
143
|
|
139
|
-
|
140
|
-
|
144
|
+
def supermodel.authenticate(lcoals = {})
|
145
|
+
p "super"
|
146
|
+
super
|
141
147
|
end
|
148
|
+
|
149
|
+
supermodel.client.expects(:call).with(:authenticate, :message => { :username => "luke", :password => "secret" })
|
150
|
+
supermodel.expects(:p).with("super") # stupid, but works
|
151
|
+
|
152
|
+
supermodel.authenticate(:message => { :username => "luke", :password => "secret" })
|
142
153
|
end
|
143
154
|
|
144
155
|
end
|