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.
Files changed (66) hide show
  1. data/CHANGELOG.md +119 -104
  2. data/README.md +12 -11
  3. data/Rakefile +0 -6
  4. data/lib/savon.rb +16 -14
  5. data/lib/savon/block_interface.rb +26 -0
  6. data/lib/savon/builder.rb +142 -0
  7. data/lib/savon/client.rb +36 -135
  8. data/lib/savon/header.rb +42 -0
  9. data/lib/savon/http_error.rb +27 -0
  10. data/lib/savon/log_message.rb +23 -25
  11. data/lib/savon/message.rb +35 -0
  12. data/lib/savon/mock.rb +5 -0
  13. data/lib/savon/mock/expectation.rb +70 -0
  14. data/lib/savon/mock/spec_helper.rb +62 -0
  15. data/lib/savon/model.rb +39 -61
  16. data/lib/savon/operation.rb +62 -0
  17. data/lib/savon/options.rb +265 -0
  18. data/lib/savon/qualified_message.rb +49 -0
  19. data/lib/savon/request.rb +92 -0
  20. data/lib/savon/response.rb +97 -0
  21. data/lib/savon/soap_fault.rb +40 -0
  22. data/lib/savon/version.rb +1 -1
  23. data/savon.gemspec +10 -8
  24. data/spec/integration/options_spec.rb +536 -0
  25. data/spec/integration/request_spec.rb +31 -16
  26. data/spec/integration/support/application.rb +80 -0
  27. data/spec/integration/support/server.rb +84 -0
  28. data/spec/savon/builder_spec.rb +81 -0
  29. data/spec/savon/client_spec.rb +90 -488
  30. data/spec/savon/http_error_spec.rb +49 -0
  31. data/spec/savon/log_message_spec.rb +33 -0
  32. data/spec/savon/mock_spec.rb +127 -0
  33. data/spec/savon/model_spec.rb +110 -99
  34. data/spec/savon/observers_spec.rb +92 -0
  35. data/spec/savon/operation_spec.rb +49 -0
  36. data/spec/savon/request_spec.rb +145 -0
  37. data/spec/savon/{soap/response_spec.rb → response_spec.rb} +22 -59
  38. data/spec/savon/soap_fault_spec.rb +94 -0
  39. data/spec/spec_helper.rb +5 -3
  40. data/spec/support/fixture.rb +5 -1
  41. metadata +202 -197
  42. data/lib/savon/config.rb +0 -46
  43. data/lib/savon/error.rb +0 -6
  44. data/lib/savon/hooks/group.rb +0 -68
  45. data/lib/savon/hooks/hook.rb +0 -61
  46. data/lib/savon/http/error.rb +0 -42
  47. data/lib/savon/logger.rb +0 -39
  48. data/lib/savon/null_logger.rb +0 -10
  49. data/lib/savon/soap.rb +0 -21
  50. data/lib/savon/soap/fault.rb +0 -59
  51. data/lib/savon/soap/invalid_response_error.rb +0 -13
  52. data/lib/savon/soap/request.rb +0 -86
  53. data/lib/savon/soap/request_builder.rb +0 -205
  54. data/lib/savon/soap/response.rb +0 -117
  55. data/lib/savon/soap/xml.rb +0 -257
  56. data/spec/savon/config_spec.rb +0 -38
  57. data/spec/savon/hooks/group_spec.rb +0 -71
  58. data/spec/savon/hooks/hook_spec.rb +0 -16
  59. data/spec/savon/http/error_spec.rb +0 -52
  60. data/spec/savon/logger_spec.rb +0 -51
  61. data/spec/savon/savon_spec.rb +0 -33
  62. data/spec/savon/soap/fault_spec.rb +0 -89
  63. data/spec/savon/soap/request_builder_spec.rb +0 -207
  64. data/spec/savon/soap/request_spec.rb +0 -112
  65. data/spec/savon/soap/xml_spec.rb +0 -357
  66. 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
@@ -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
- let(:model) do
6
- Class.new { extend Savon::Model }
6
+ before :all do
7
+ @server = IntegrationServer.run
7
8
  end
8
9
 
9
- describe ".client" do
10
- it "memoizes the Savon::Client" do
11
- model.client.should equal(model.client)
12
- end
10
+ after :all do
11
+ @server.stop
13
12
  end
14
13
 
15
- describe ".endpoint" do
16
- it "sets the SOAP endpoint" do
17
- model.endpoint "http://example.com"
18
- model.client.wsdl.endpoint.should == "http://example.com"
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
- describe ".namespace" do
23
- it "sets the target namespace" do
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
- describe ".document" do
30
- it "sets the WSDL document" do
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 ".headers" do
37
- it "sets the HTTP headers" do
38
- model.headers("Accept-Charset" => "utf-8")
39
- model.client.http.headers.should == { "Accept-Charset" => "utf-8" }
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
- describe ".basic_auth" do
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
- describe ".wsse_auth" do
51
- it "sets WSSE auth credentials" do
52
- model.wsse_auth "login", "password", :digest
40
+ global :soap_version, 2
41
+ global :open_timeout, 71
42
+ global :wsse_auth, "luke", "secret", :digest
43
+ }
53
44
 
54
- model.client.wsse.username.should == "login"
55
- model.client.wsse.password.should == "password"
56
- model.client.wsse.should be_digest
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 ".actions" do
61
- before(:all) do
62
- model.actions :get_user, "GetAllUsers"
63
- end
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
- it "defines class methods each action" do
66
- model.should respond_to(:get_user, :get_all_users)
67
- end
57
+ client :wsdl => Fixture.wsdl(:authentication)
58
+ operations :authenticate
59
+ }
68
60
 
69
- it "defines instance methods each action" do
70
- model.new.should respond_to(:get_user, :get_all_users)
61
+ expect(model).to respond_to(:authenticate)
71
62
  end
72
63
 
73
- context "(class-level)" do
74
- it "executes SOAP requests with a given body" do
75
- model.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
76
- model.get_user :id => 1
77
- end
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
- it "accepts and passes Strings for action names" do
80
- model.client.expects(:request).with(:wsdl, "GetAllUsers", :body => { :id => 1 })
81
- model.get_all_users :id => 1
82
- end
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
- context "(instance-level)" do
86
- it "delegates to the corresponding class method" do
87
- model.expects(:get_all_users).with(:active => true)
88
- model.new.get_all_users :active => true
89
- end
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
- describe "#client" do
94
- it "returns the class-level Savon::Client" do
95
- model.new.client.should == model.client
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
- describe "overwriting action methods" do
100
- context "(class-level)" do
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
- supermodel
111
- end
113
+ model = Class.new {
114
+ extend Savon::Model
115
+ client :endpoint => repeat_url, :namespace => "http://v1.example.com"
116
+ }
112
117
 
113
- it "works" do
114
- supermodel.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
115
- supermodel.expects(:p).with("super") # stupid, but works
118
+ supermodel = model.dup
119
+ supermodel.operations :authenticate
116
120
 
117
- supermodel.get_user :id => 1
118
- end
121
+ def supermodel.authenticate(locals = {})
122
+ p "super"
123
+ super
119
124
  end
120
125
 
121
- context "(instance-level)" do
122
- let(:supermodel) do
123
- supermodel = model.dup
124
- supermodel.actions :get_user
125
- supermodel = supermodel.new
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
- def supermodel.get_user(body = nil, &block)
128
- p "super"
129
- super
130
- end
132
+ it "allows to overwrite instance operations" do
133
+ repeat_url = @server.url(:repeat)
131
134
 
132
- supermodel
133
- end
135
+ model = Class.new {
136
+ extend Savon::Model
137
+ client :endpoint => repeat_url, :namespace => "http://v1.example.com"
138
+ }
134
139
 
135
- it "works" do
136
- supermodel.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
137
- supermodel.expects(:p).with("super") # stupid, but works
140
+ supermodel = model.dup
141
+ supermodel.operations :authenticate
142
+ supermodel = supermodel.new
138
143
 
139
- supermodel.get_user :id => 1
140
- end
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