wash_out_fork 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +36 -0
  5. data/Appraisals +25 -0
  6. data/CHANGELOG.md +102 -0
  7. data/Gemfile +17 -0
  8. data/Guardfile +12 -0
  9. data/LICENSE +22 -0
  10. data/README.md +242 -0
  11. data/Rakefile +25 -0
  12. data/app/helpers/wash_out_fork_helper.rb +110 -0
  13. data/app/views/wash_out_fork/document/error.builder +9 -0
  14. data/app/views/wash_out_fork/document/response.builder +8 -0
  15. data/app/views/wash_out_fork/document/wsdl.builder +68 -0
  16. data/app/views/wash_out_fork/rpc/error.builder +10 -0
  17. data/app/views/wash_out_fork/rpc/response.builder +9 -0
  18. data/app/views/wash_out_fork/rpc/wsdl.builder +68 -0
  19. data/gemfiles/rails_3.2.13.gemfile +21 -0
  20. data/gemfiles/rails_4.0.0.gemfile +20 -0
  21. data/gemfiles/rails_4.1.0.gemfile +20 -0
  22. data/gemfiles/rails_4.2.0.gemfile +20 -0
  23. data/gemfiles/rails_5.0.0.beta2.gemfile +19 -0
  24. data/gemfiles/rails_5.0.0.gemfile +19 -0
  25. data/init.rb +1 -0
  26. data/lib/wash_out_fork.rb +60 -0
  27. data/lib/wash_out_fork/configurable.rb +41 -0
  28. data/lib/wash_out_fork/dispatcher.rb +232 -0
  29. data/lib/wash_out_fork/engine.rb +12 -0
  30. data/lib/wash_out_fork/middleware.rb +41 -0
  31. data/lib/wash_out_fork/model.rb +29 -0
  32. data/lib/wash_out_fork/param.rb +200 -0
  33. data/lib/wash_out_fork/router.rb +131 -0
  34. data/lib/wash_out_fork/soap.rb +48 -0
  35. data/lib/wash_out_fork/soap_config.rb +93 -0
  36. data/lib/wash_out_fork/type.rb +29 -0
  37. data/lib/wash_out_fork/version.rb +3 -0
  38. data/lib/wash_out_fork/wsse.rb +101 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  43. data/spec/dummy/config.ru +4 -0
  44. data/spec/dummy/config/application.rb +51 -0
  45. data/spec/dummy/config/boot.rb +10 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +23 -0
  48. data/spec/dummy/config/environments/test.rb +30 -0
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/dummy/config/initializers/inflections.rb +10 -0
  51. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  52. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  53. data/spec/dummy/config/initializers/session_store.rb +8 -0
  54. data/spec/dummy/config/locales/en.yml +5 -0
  55. data/spec/dummy/config/routes.rb +58 -0
  56. data/spec/dummy/public/404.html +26 -0
  57. data/spec/dummy/public/422.html +26 -0
  58. data/spec/dummy/public/500.html +26 -0
  59. data/spec/dummy/public/favicon.ico +0 -0
  60. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  61. data/spec/dummy/script/rails +6 -0
  62. data/spec/fixtures/nested_refs_to_arrays.xml +19 -0
  63. data/spec/fixtures/ref_to_one_array.xml +11 -0
  64. data/spec/fixtures/refs_to_arrays.xml +16 -0
  65. data/spec/lib/wash_out/dispatcher_spec.rb +206 -0
  66. data/spec/lib/wash_out/middleware_spec.rb +33 -0
  67. data/spec/lib/wash_out/param_spec.rb +94 -0
  68. data/spec/lib/wash_out/router_spec.rb +50 -0
  69. data/spec/lib/wash_out/type_spec.rb +41 -0
  70. data/spec/lib/wash_out_spec.rb +797 -0
  71. data/spec/spec_helper.rb +88 -0
  72. data/wash_out_fork.gemspec +20 -0
  73. metadata +130 -0
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'wash_out_fork/middleware'
3
+ require 'rexml/document'
4
+
5
+ describe WashOutFork::Middleware do
6
+ it 'handles Rack environment variables' do
7
+ err = begin
8
+ REXML::Document.new '<hi>'
9
+ rescue REXML::ParseException => e
10
+ e
11
+ end
12
+
13
+ env = {}
14
+ expect {
15
+ WashOutFork::Middleware.raise_or_render_rexml_parse_error err, env
16
+ }.to raise_exception(REXML::ParseException)
17
+
18
+ env['HTTP_SOAPACTION'] = 'pretend_action'
19
+ env['rack.errors'] = double 'logger', {:puts => true}
20
+ env['rack.input'] = double 'basic-rack-input', {:string => '<hi>'}
21
+ result = WashOutFork::Middleware.raise_or_render_rexml_parse_error err, env
22
+ expect(result[0]).to eq 400
23
+ expect(result[1]['Content-Type']).to eq 'text/xml'
24
+ msg = result[2][0]
25
+ expect(msg).to include 'Error parsing SOAP Request XML'
26
+ expect(msg).to include 'soap:Fault'
27
+ expect(msg).not_to include __FILE__
28
+
29
+ env['rack.input'] = double 'passenger-input', {:read => '<hi>'}
30
+ result = WashOutFork::Middleware.raise_or_render_rexml_parse_error err, env
31
+ expect(result[0]).to eq 400
32
+ end
33
+ end
@@ -0,0 +1,94 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOutFork::Param do
6
+
7
+ context "custom types" do
8
+
9
+ class Abraka1 < WashOutFork::Type
10
+ map(
11
+ :test => :string
12
+ )
13
+ end
14
+
15
+ class Abraka2 < WashOutFork::Type
16
+ type_name 'test'
17
+ map :foo => Abraka1
18
+ end
19
+
20
+ it "loads custom_types" do
21
+ soap_config = WashOutFork::SoapConfig.new({ camelize_wsdl: false })
22
+ map = WashOutFork::Param.parse_def soap_config, Abraka2
23
+
24
+ expect(map).to be_a_kind_of(Array)
25
+ expect(map[0].name).to eq 'foo'
26
+ expect(map[0].map[0].name).to eq 'test'
27
+ end
28
+
29
+ it "respects camelization setting" do
30
+ soap_config = WashOutFork::SoapConfig.new({ camelize_wsdl: true })
31
+
32
+ map = WashOutFork::Param.parse_def soap_config, Abraka2
33
+
34
+ expect(map).to be_a_kind_of(Array)
35
+ expect(map[0].name).to eq 'Foo'
36
+ expect(map[0].map[0].name).to eq 'Test'
37
+ end
38
+ end
39
+
40
+ it "should accept nested empty arrays" do
41
+ soap_config = WashOutFork::SoapConfig.new({ camelize_wsdl: false })
42
+ map = WashOutFork::Param.parse_def(soap_config, {:nested => {:some_attr => :string, :empty => [:integer] }} )
43
+ expect(map[0].load( {:nested => nil}, :nested)).to eq({})
44
+ end
45
+
46
+ describe "booleans" do
47
+ let(:soap_config) { WashOutFork::SoapConfig.new({ camelize_wsdl: false }) }
48
+ # following http://www.w3.org/TR/xmlschema-2/#boolean, only true, false, 0 and 1 are allowed.
49
+ # Nori maps the strings true and false to TrueClass and FalseClass, but not 0 and 1.
50
+ let(:map) { WashOutFork::Param.parse_def(soap_config, :value => :boolean) }
51
+
52
+ it "should accept 'true' and '1'" do
53
+ expect(map[0].load({:value => true}, :value)).to be true
54
+ expect(map[0].load({:value => "1"}, :value)).to be true
55
+ end
56
+
57
+ it "should accept 'false' and '0'" do
58
+ expect(map[0].load({:value => false}, :value)).to be false
59
+ expect(map[0].load({:value => "0"}, :value)).to be false
60
+ end
61
+ end
62
+
63
+ describe 'longs' do
64
+ let(:soap_config) { WashOutFork::SoapConfig.new({ camelize_wsdl: false }) }
65
+ let(:map) { WashOutFork::Param.parse_def(soap_config, :value => :long) }
66
+
67
+ it "should accept positive long" do
68
+ expect(map[0].load({:value => 9223372036854775807}, :value)).to eq 9223372036854775807
69
+ end
70
+
71
+ it "should accept negative long" do
72
+ expect(map[0].load({:value => -9223372036854775807}, :value)).to eq -9223372036854775807
73
+ end
74
+ end
75
+
76
+ describe '#flat_copy' do
77
+ it 'should copy everything' do
78
+ soap_config = WashOutFork::SoapConfig.new({})
79
+ type = :foo
80
+ multiplied = "of course"
81
+
82
+ param = WashOutFork::Param.new(soap_config, 'name', type, multiplied)
83
+ param.source_class = "middle class"
84
+ evil_clone = param.flat_copy
85
+
86
+ expect(evil_clone.source_class).to eq "middle class"
87
+ expect(evil_clone.name).to eq 'name'
88
+ expect(evil_clone.raw_name).to eq 'name'
89
+ expect(evil_clone.type).to eq "foo"
90
+ expect(evil_clone.multiplied).to eq "of course"
91
+ expect(evil_clone.soap_config).to eq soap_config
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'wash_out_fork/router'
3
+
4
+ describe WashOutFork::Router do
5
+ it 'returns a 200 with empty soap action' do
6
+
7
+ mock_controller do
8
+ # nothing
9
+ end
10
+
11
+ env = {}
12
+ env['REQUEST_METHOD'] = 'GET'
13
+ env['rack.input'] = double 'basic-rack-input', {:string => ''}
14
+ result = WashOutFork::Router.new('Route::Space::Api').call env
15
+
16
+ expect(result[0]).to eq(500)
17
+ expect(result[1]['Content-Type']).to eq('text/xml; charset=utf-8')
18
+ end
19
+
20
+ def parse_soap_params_from_xml(filename)
21
+ xml = File.read(File.expand_path("../../../fixtures/#{filename}", __FILE__))
22
+ env = {'rack.input' => StringIO.new(xml)}
23
+
24
+ router = WashOutFork::Router.new('')
25
+ controller = double("controller", soap_config: WashOutFork::SoapConfig.new)
26
+ allow(router).to receive(:controller).and_return(controller)
27
+
28
+ router.parse_soap_parameters(env)[:Envelope][:Body]
29
+ end
30
+
31
+ it "returns refs to arrays correctly" do
32
+ body = parse_soap_params_from_xml('ref_to_one_array.xml')
33
+
34
+ expect(body[:list][:Item]).to eq(["1", "2"])
35
+ end
36
+
37
+ it "returns refs to multiple arrays correctly" do
38
+ body = parse_soap_params_from_xml('refs_to_arrays.xml')
39
+
40
+ expect(body[:first_list][:Item]).to eq(["1", "2"])
41
+ expect(body[:second_list][:Item]).to eq(["11", "22"])
42
+ end
43
+
44
+ it "returns nested refs to multiple arrays correctly" do
45
+ body = parse_soap_params_from_xml('nested_refs_to_arrays.xml')
46
+
47
+ expect(body[:parent][:first_list][:Item]).to eq(["1", "2"])
48
+ expect(body[:parent][:second_list][:Item]).to eq(["11", "22"])
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOutFork::Type do
6
+
7
+ it "defines custom type" do
8
+
9
+ class Abraka1 < WashOutFork::Type
10
+ map :test => :string
11
+ end
12
+
13
+ class Abraka2 < WashOutFork::Type
14
+ type_name 'test'
15
+ map :foo => Abraka1
16
+ end
17
+
18
+ expect(Abraka1.wash_out_fork_param_name).to eq 'abraka1'
19
+ expect(Abraka1.wash_out_fork_param_map).to eq({:test => :string})
20
+
21
+ expect(Abraka2.wash_out_fork_param_name).to eq 'test'
22
+ expect(Abraka2.wash_out_fork_param_map).to eq({:foo => Abraka1})
23
+ end
24
+
25
+ it "allows arrays inside custom types" do
26
+ class Abraka1 < WashOutFork::Type
27
+ map :test => :string
28
+ end
29
+ class Abraka2 < WashOutFork::Type
30
+ type_name 'test'
31
+ map :foo => [:bar => Abraka1]
32
+ end
33
+
34
+ expect(Abraka1.wash_out_fork_param_name).to eq 'abraka1'
35
+ expect(Abraka1.wash_out_fork_param_map).to eq({:test => :string})
36
+
37
+ expect(Abraka2.wash_out_fork_param_name).to eq 'test'
38
+ expect(Abraka2.wash_out_fork_param_map).to eq({:foo => [:bar => Abraka1]})
39
+ end
40
+
41
+ end
@@ -0,0 +1,797 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOutFork do
6
+
7
+ let :nori do
8
+ Nori.new(
9
+ :strip_namespaces => true,
10
+ :advanced_typecasting => true,
11
+ :convert_tags_to => lambda {|x| x.snakecase.to_sym}
12
+ )
13
+ end
14
+
15
+ def savon(method, message={}, hashify=true, &block)
16
+ message = {:value => message} unless message.is_a?(Hash)
17
+
18
+ savon = Savon::Client.new(:log => false, :wsdl => 'http://app/route/api/wsdl', &block)
19
+ result = savon.call(method, :message => message)
20
+ result = result.to_hash if hashify
21
+ result
22
+ end
23
+
24
+ def savon!(method, message={}, &block)
25
+ message = {:value => message} unless message.is_a?(Hash)
26
+
27
+ savon = Savon::Client.new(:log => true, :wsdl => 'http://app/route/api/wsdl', &block)
28
+ savon.call(method, :message => message).to_hash
29
+ end
30
+
31
+ describe "Module" do
32
+ it "includes" do
33
+ expect {
34
+ mock_controller do
35
+ # nothing
36
+ end
37
+ }.not_to raise_exception
38
+ end
39
+
40
+ it "allows definition of a simple action" do
41
+ expect {
42
+ mock_controller do
43
+ soap_action "answer", :args => nil, :return => :integer
44
+ end
45
+ }.not_to raise_exception
46
+ end
47
+ end
48
+
49
+ describe "WSDL" do
50
+ let :wsdl do
51
+ mock_controller do
52
+ soap_action :result, :args => nil, :return => :int
53
+
54
+ soap_action "getArea", :args => {
55
+ :circle => [{
56
+ :center => { :x => [:integer], :y => :integer },
57
+ :radius => :double
58
+ }]},
59
+ :return => { :area => :double }
60
+ soap_action "rocky", :args => { :circle1 => { :x => :integer } },
61
+ :return => { :circle2 => { :y => :integer } }
62
+ end
63
+
64
+ HTTPI.get("http://app/route/api/wsdl").body
65
+ end
66
+
67
+ let :xml do
68
+ nori.parse wsdl
69
+ end
70
+
71
+ it "lists operations" do
72
+ operations = xml[:definitions][:binding][:operation]
73
+ expect(operations).to be_a_kind_of(Array)
74
+
75
+ expect(operations.map{|e| e[:'@name']}.sort).to eq ['Result', 'getArea', 'rocky'].sort
76
+ end
77
+
78
+ it "defines complex types" do
79
+ expect(wsdl.include?('<xsd:complexType name="Circle1">')).to be true
80
+ end
81
+
82
+ it "defines arrays" do
83
+ x = xml[:definitions][:types][:schema][:complex_type].
84
+ find{|x| x[:'@name'] == 'Center'}[:sequence][:element].
85
+ find{|x| x[:'@name'] == 'X'}
86
+
87
+ expect(x[:'@min_occurs']).to eq "0"
88
+ expect(x[:'@max_occurs']).to eq "unbounded"
89
+ expect(x[:'@nillable']).to eq "true"
90
+ end
91
+
92
+ it "adds nillable to all type definitions" do
93
+ types = xml[:definitions][:message].map { |d| d[:part] }.compact
94
+ nillable = types.map { |t| t[:"@xsi:nillable"] }
95
+ expect(nillable.all? { |v| v == "true" }).to be true
96
+ end
97
+ end
98
+
99
+ describe "Dispatcher" do
100
+
101
+ context "simple actions" do
102
+ it "accepts requests with no HTTP header" do
103
+ mock_controller do
104
+ soap_action "answer", :args => nil, :return => :int
105
+ def answer
106
+ render :soap => "42"
107
+ end
108
+ end
109
+
110
+ request = <<-XML
111
+ <?xml version="1.0" encoding="UTF-8"?>
112
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="false" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
113
+ <env:Body>
114
+ <tns:answer>
115
+ <value>42</value>
116
+ </tns:answer>
117
+ </env:Body>
118
+ </env:Envelope>
119
+ XML
120
+
121
+ expect(HTTPI.post("http://app/route/api/action", request).body).to eq <<-XML
122
+ <?xml version="1.0" encoding="UTF-8"?>
123
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="false">
124
+ <soap:Body>
125
+ <tns:answerResponse>
126
+ <Value xsi:type="xsd:int">42</Value>
127
+ </tns:answerResponse>
128
+ </soap:Body>
129
+ </soap:Envelope>
130
+ XML
131
+ end
132
+
133
+ it "accept no parameters" do
134
+ mock_controller do
135
+ soap_action "answer", :args => nil, :return => :int
136
+ def answer
137
+ render :soap => "42"
138
+ end
139
+ end
140
+
141
+ expect(savon(:answer)[:answer_response][:value]).
142
+ to eq "42"
143
+ end
144
+
145
+ it "accept insufficient parameters" do
146
+ mock_controller do
147
+ soap_action "answer", :args => {:a => :integer}, :return => :integer
148
+ def answer
149
+ render :soap => "42"
150
+ end
151
+ end
152
+
153
+ expect(savon(:answer)[:answer_response][:value]).
154
+ to eq "42"
155
+ end
156
+
157
+ it "shows date in correct format" do
158
+ mock_controller do
159
+ soap_action "answer", :args => {}, :return => {:a => :date}
160
+ def answer
161
+ render :soap => {:a => DateTime.new(2000, 1, 1)}
162
+ end
163
+ end
164
+ result = Hash.from_xml savon(:answer, {}, false).http.body
165
+ expect(result['Envelope']['Body']['answerResponse']['A']).to eq '2000-01-01T00:00:00+00:00'
166
+ end
167
+
168
+ it "accept empty parameter" do
169
+ mock_controller do
170
+ soap_action "answer", :args => {:a => :string}, :return => {:a => :string}
171
+ def answer
172
+ render :soap => {:a => params[:a]}
173
+ end
174
+ end
175
+ expect(savon(:answer, :a => '')[:answer_response][:a]).to be_nil
176
+ end
177
+
178
+ it "accept one parameter" do
179
+ mock_controller do
180
+ soap_action "checkAnswer", :args => :integer, :return => :boolean, :to => 'check_answer'
181
+ def check_answer
182
+ render :soap => (params[:value] == 42)
183
+ end
184
+ end
185
+
186
+ expect(savon(:check_answer, 42)[:check_answer_response][:value]).to be true
187
+ expect(savon(:check_answer, 13)[:check_answer_response][:value]).to be false
188
+ end
189
+
190
+ it "accept two parameters" do
191
+ mock_controller do
192
+ soap_action "funky", :args => { :a => :integer, :b => :string }, :return => :string
193
+ def funky
194
+ render :soap => ((params[:a] * 10).to_s + params[:b])
195
+ end
196
+ end
197
+
198
+ expect(savon(:funky, :a => 42, :b => 'k')[:funky_response][:value]).to eq '420k'
199
+ end
200
+ end
201
+
202
+ context "complex actions" do
203
+ it "accept nested structures" do
204
+ mock_controller do
205
+ soap_action "getArea", :args => { :circle => { :center => { :x => :integer,
206
+ :y => :integer },
207
+ :radius => :double } },
208
+ :return => { :area => :double,
209
+ :distance_from_o => :double },
210
+ :to => :get_area
211
+ def get_area
212
+ circle = params[:circle]
213
+ render :soap => { :area => Math::PI * circle[:radius] ** 2,
214
+ :distance_from_o => Math.sqrt(circle[:center][:x] ** 2 + circle[:center][:y] ** 2) }
215
+ end
216
+ end
217
+
218
+ message = { :circle => { :center => { :x => 3, :y => 4 },
219
+ :radius => 5 } }
220
+
221
+ expect(savon(:get_area, message)[:get_area_response]).
222
+ to eq ({ :area => (Math::PI * 25).to_s, :distance_from_o => (5.0).to_s })
223
+ end
224
+
225
+ it "accept arrays" do
226
+ mock_controller do
227
+ soap_action "rumba",
228
+ :args => {
229
+ :rumbas => [:integer]
230
+ },
231
+ :return => nil
232
+ def rumba
233
+ expect(params).to eq({"rumbas" => [1, 2, 3]})
234
+ render :soap => nil
235
+ end
236
+ end
237
+
238
+ savon(:rumba, :rumbas => [1, 2, 3])
239
+ end
240
+
241
+ it "accept empty arrays" do
242
+ mock_controller do
243
+ soap_action "rumba",
244
+ :args => {
245
+ :my_array => [:integer]
246
+ },
247
+ :return => nil
248
+ def rumba
249
+ expect(params).to eq({})
250
+ render :soap => nil
251
+ end
252
+ end
253
+ savon(:rumba, :empty => [])
254
+ end
255
+
256
+ it "accept nested empty arrays" do
257
+ mock_controller do
258
+ soap_action "rumba",
259
+ :args => {
260
+ :nested => {:my_array => [:integer] }
261
+ },
262
+ :return => nil
263
+ def rumba
264
+ expect(params).to eq({"nested" => {}})
265
+ render :soap => nil
266
+ end
267
+ end
268
+ savon(:rumba, :nested => {:my_array => []})
269
+ end
270
+
271
+ it "accept nested structures inside arrays" do
272
+ mock_controller do
273
+ soap_action "rumba",
274
+ :args => {
275
+ :rumbas => [ {
276
+ :zombies => :string,
277
+ :puppies => :string
278
+ } ]
279
+ },
280
+ :return => nil
281
+ def rumba
282
+ expect(params).to eq({
283
+ "rumbas" => [
284
+ {"zombies" => 'suck', "puppies" => 'rock'},
285
+ {"zombies" => 'slow', "puppies" => 'fast'}
286
+ ]
287
+ })
288
+ render :soap => nil
289
+ end
290
+ end
291
+
292
+ savon :rumba, :rumbas => [
293
+ {:zombies => 'suck', :puppies => 'rock'},
294
+ {:zombies => 'slow', :puppies => 'fast'}
295
+ ]
296
+ end
297
+
298
+ it "respond with nested structures" do
299
+ mock_controller do
300
+ soap_action "gogogo",
301
+ :args => nil,
302
+ :return => {
303
+ :zoo => :string,
304
+ :boo => { :moo => :string, :doo => :string }
305
+ }
306
+ def gogogo
307
+ render :soap => {
308
+ :zoo => 'zoo',
309
+ :boo => { :moo => 'moo', :doo => 'doo' }
310
+ }
311
+ end
312
+ end
313
+
314
+ expect(savon(:gogogo)[:gogogo_response]).
315
+ to eq({
316
+ :zoo=>"zoo",
317
+ :boo=>{
318
+ :moo=>"moo",
319
+ :doo=>"doo",
320
+ :"@xsi:type"=>"tns:Boo"
321
+ }
322
+ })
323
+ end
324
+
325
+ it "respond with arrays" do
326
+ mock_controller do
327
+ soap_action "rumba",
328
+ :args => nil,
329
+ :return => [:integer]
330
+ def rumba
331
+ render :soap => [1, 2, 3]
332
+ end
333
+ end
334
+
335
+ expect(savon(:rumba)[:rumba_response]).to eq({
336
+ :value => ["1", "2", "3"]
337
+ })
338
+ end
339
+
340
+ it "respond with complex structures inside arrays" do
341
+ mock_controller do
342
+ soap_action "rumba",
343
+ :args => nil,
344
+ :return => {
345
+ :rumbas => [{:@level => :integer, :zombies => :string, :puppies => :string}]
346
+ }
347
+ def rumba
348
+ render :soap =>
349
+ {:rumbas => [
350
+ {:@level => 80, :zombies => "suck1", :puppies => "rock1" },
351
+ {:zombies => "suck2", :puppies => "rock2" }
352
+ ]
353
+ }
354
+ end
355
+ end
356
+
357
+ expect(savon(:rumba)[:rumba_response]).to eq({
358
+ :rumbas => [
359
+ {:zombies => "suck1",:puppies => "rock1", :"@xsi:type"=>"tns:Rumbas", :@level => "80"},
360
+ {:zombies => "suck2", :puppies => "rock2", :"@xsi:type"=>"tns:Rumbas" }
361
+ ]
362
+ })
363
+ end
364
+
365
+ it "respond with structs in structs in arrays" do
366
+ mock_controller do
367
+ soap_action "rumba",
368
+ :args => nil,
369
+ :return => [{:rumbas => {:@level => :integer, :zombies => :integer}}]
370
+
371
+ def rumba
372
+ render :soap => [{:rumbas => {:@level => 80, :zombies => 100000}}, {:rumbas => {:@level => 90, :zombies => 2}}]
373
+ end
374
+ end
375
+
376
+ expect(savon(:rumba)[:rumba_response]).to eq({
377
+ :value => [
378
+ {
379
+ :rumbas => {
380
+ :zombies => "100000",
381
+ :"@xsi:type" => "tns:Rumbas",
382
+ :"@level" => "80"
383
+ },
384
+ :"@xsi:type" => "tns:Value"
385
+ },
386
+ {
387
+ :rumbas => {
388
+ :zombies => "2",
389
+ :"@xsi:type" => "tns:Rumbas",
390
+ :@level => "90",
391
+ },
392
+ :"@xsi:type"=>"tns:Value"
393
+ }
394
+ ]
395
+ })
396
+ end
397
+
398
+ context "with arrays missing" do
399
+ it "respond with simple definition" do
400
+ mock_controller do
401
+ soap_action "rocknroll",
402
+ :args => nil, :return => { :my_value => [:integer] }
403
+ def rocknroll
404
+ render :soap => {}
405
+ end
406
+ end
407
+
408
+ expect(savon(:rocknroll)[:rocknroll_response]).to be nil
409
+ end
410
+
411
+ it "respond with complext definition" do
412
+ mock_controller do
413
+ soap_action "rocknroll",
414
+ :args => nil, :return => { :my_value => [{ :value => :integer }] }
415
+ def rocknroll
416
+ render :soap => {}
417
+ end
418
+ end
419
+
420
+ expect(savon(:rocknroll)[:rocknroll_response]).to be nil
421
+ end
422
+
423
+ it "respond with nested simple definition" do
424
+ mock_controller do
425
+ soap_action "rocknroll",
426
+ :args => nil, :return => { :my_value => { :my_array => [{ :value => :integer }] } }
427
+ def rocknroll
428
+ render :soap => {}
429
+ end
430
+ end
431
+
432
+ expect(savon(:rocknroll)[:rocknroll_response][:my_value]).to be_nil
433
+ end
434
+
435
+ it "responds with missing parameters" do
436
+ mock_controller do
437
+ soap_action "rocknroll",
438
+ args: nil,
439
+ return: {my_value: :integer}
440
+ def rocknroll
441
+ render soap: {my_value: nil}
442
+ end
443
+ end
444
+
445
+ expect(savon(:rocknroll)[:rocknroll_response][:my_value]).to be_nil
446
+ end
447
+
448
+ it "handles incomplete array response" do
449
+ mock_controller do
450
+ soap_action "rocknroll",
451
+ :args => nil, :return => { :my_value => [{ :value => :string }] }
452
+ def rocknroll
453
+ render :soap => { :my_value => [nil] }
454
+ end
455
+ end
456
+
457
+ expect{savon(:rocknroll)}.not_to raise_error
458
+ end
459
+ end
460
+ end
461
+
462
+ context "types" do
463
+ it "recognize boolean" do
464
+ mock_controller do
465
+ soap_action "true", :args => :boolean, :return => :nil
466
+ def true
467
+ expect(params[:value]).to be true
468
+ render :soap => nil
469
+ end
470
+
471
+ soap_action "false", :args => :boolean, :return => :nil
472
+ def false
473
+ expect(params[:value]).to be false
474
+ render :soap => nil
475
+ end
476
+ end
477
+
478
+ savon(:true, :value => "true")
479
+ savon(:true, :value => "1")
480
+ savon(:false, :value => "false")
481
+ savon(:false, :value => "0")
482
+ end
483
+
484
+ it "recognize dates" do
485
+ mock_controller do
486
+ soap_action "date", :args => :date, :return => :nil
487
+ def date
488
+ expect(params[:value]).to eq Date.parse('2000-12-30') unless params[:value].blank?
489
+ render :soap => nil
490
+ end
491
+ end
492
+
493
+ savon(:date, :value => '2000-12-30')
494
+ expect { savon(:date) }.not_to raise_exception
495
+ end
496
+
497
+ it "recognize base64Binary" do
498
+ mock_controller do
499
+ soap_action "base64", :args => :base64Binary, :return => :nil
500
+ def base64
501
+ expect(params[:value]).to eq('test') unless params[:value].blank?
502
+ render :soap => nil
503
+ end
504
+ end
505
+
506
+ savon(:base64, :value => Base64.encode64('test'))
507
+ expect { savon(:base64) }.not_to raise_exception
508
+ end
509
+ end
510
+
511
+ context "errors" do
512
+ it "raise for incorrect requests" do
513
+ mock_controller do
514
+ soap_action "duty",
515
+ :args => {:bad => {:a => :string, :b => :string}, :good => {:a => :string, :b => :string}},
516
+ :return => nil
517
+ def duty
518
+ render :soap => nil
519
+ end
520
+ end
521
+
522
+ expect {
523
+ savon(:duty, :bad => 42, :good => nil)
524
+ }.to raise_exception(Savon::SOAPFault)
525
+ end
526
+
527
+ it "raise for date in incorrect format" do
528
+ mock_controller do
529
+ soap_action "date", :args => :date, :return => :nil
530
+ def date
531
+ render :soap => nil
532
+ end
533
+ end
534
+ expect {
535
+ savon(:date, :value => 'incorrect format')
536
+ }.to raise_exception(Savon::SOAPFault)
537
+ end
538
+
539
+ it "raise to report SOAP errors" do
540
+ mock_controller do
541
+ soap_action "error", :args => { :need_error => :boolean }, :return => nil
542
+ def error
543
+ raise self.class.const_get(:SOAPError), "you wanted one" if params[:need_error]
544
+ render :soap => nil
545
+ end
546
+ end
547
+
548
+ expect { savon(:error, :need_error => false) }.not_to raise_exception
549
+ expect { savon(:error, :need_error => true) }.to raise_exception(Savon::SOAPFault)
550
+ end
551
+
552
+ it "misses basic exceptions" do
553
+ mock_controller do
554
+ soap_action "error", :args => { :need_error => :boolean }, :return => nil
555
+ def error
556
+ raise self.class.const_get(:Exception), "you wanted one" if params[:need_error]
557
+ render :soap => nil
558
+ end
559
+ end
560
+
561
+ expect { savon(:error, :need_error => false) }.not_to raise_exception
562
+ expect { savon(:error, :need_error => true) }.to raise_exception(Exception)
563
+ end
564
+
565
+ it "raise for manual throws" do
566
+ mock_controller do
567
+ soap_action "error", :args => nil, :return => nil
568
+ def error
569
+ render_soap_error "a message"
570
+ end
571
+ end
572
+
573
+ expect { savon(:error) }.to raise_exception(Savon::SOAPFault)
574
+ end
575
+
576
+ it "raise when response structure mismatches" do
577
+ mock_controller do
578
+ soap_action 'bad', :args => :integer, :return => {
579
+ :basic => :string,
580
+ :stallions => {
581
+ :stallion => [
582
+ :name => :string,
583
+ :wyldness => :integer,
584
+ ]
585
+ },
586
+ }
587
+ def bad
588
+ render :soap => {
589
+ :basic => 'hi',
590
+ :stallions => [{:name => 'ted', :wyldness => 11}]
591
+ }
592
+ end
593
+
594
+ soap_action 'bad2', :args => :integer, :return => {
595
+ :basic => :string,
596
+ :telephone_booths => [:string]
597
+ }
598
+ def bad2
599
+ render :soap => {
600
+ :basic => 'hihi',
601
+ :telephone_booths => 'oops'
602
+ }
603
+ end
604
+ end
605
+
606
+ expect { savon(:bad) }.to raise_exception(
607
+ WashOutFork::Dispatcher::ProgrammerError,
608
+ /SOAP response .*wyldness.*Array.*Hash.*stallion/
609
+ )
610
+
611
+ expect { savon(:bad2) }.to raise_exception(
612
+ WashOutFork::Dispatcher::ProgrammerError,
613
+ /SOAP response .*oops.*String.*telephone_booths.*Array/
614
+ )
615
+ end
616
+ end
617
+
618
+ context "deprecates" do
619
+ # This test uses deprecated rspec expectations
620
+ # and it's not clear how to rewrite it.
621
+ xit "old syntax" do
622
+ # save rspec context check
623
+ raise_runtime_exception = raise_exception(RuntimeError)
624
+
625
+ mock_controller do
626
+
627
+ lambda {
628
+ soap_action "rumba",
629
+ :args => :integer,
630
+ :return => []
631
+ }.should raise_runtime_exception
632
+ def rumba
633
+ render :soap => nil
634
+ end
635
+ end
636
+ end
637
+ end
638
+
639
+ it "allows arbitrary action names" do
640
+ name = 'AnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything'
641
+
642
+ mock_controller do
643
+ soap_action name, :args => nil, :return => :integer, :to => :answer
644
+ def answer
645
+ render :soap => "forty two"
646
+ end
647
+ end
648
+
649
+ expect(savon(name.underscore.to_sym)["#{name.underscore}_response".to_sym][:value]).to eq "forty two"
650
+ end
651
+
652
+ it "respects :response_tag option" do
653
+ mock_controller do
654
+ soap_action "specific", :response_tag => "test", :return => :string
655
+ def specific
656
+ render :soap => "test"
657
+ end
658
+ end
659
+
660
+ expect(savon(:specific)).to eq({:test => {:value=>"test"}})
661
+ end
662
+
663
+ it "handles snakecase option properly" do
664
+ mock_controller(snakecase_input: false, camelize_wsdl: false) do
665
+ soap_action "rocknroll", :args => {:ZOMG => :string}, :return => nil
666
+ def rocknroll
667
+ expect(params["ZOMG"]).to eq "yam!"
668
+ render :soap => nil
669
+ end
670
+ end
671
+
672
+ savon(:rocknroll, "ZOMG" => 'yam!')
673
+ end
674
+ end
675
+
676
+ describe "Router" do
677
+ it "raises when SOAP message without SOAP Envelope arrives" do
678
+ mock_controller do; end
679
+ invalid_request = '<a></a>'
680
+ response_hash = Nori.new.parse(HTTPI.post("http://app/route/api/action", invalid_request).body)
681
+ expect(response_hash["soap:Envelope"]["soap:Body"]["soap:Fault"]['faultstring']).to eq "Invalid SOAP request"
682
+ end
683
+
684
+ it "raises when SOAP message without SOAP Body arrives" do
685
+ mock_controller do; end
686
+ invalid_request = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"></s:Envelope>'
687
+ response_hash = Nori.new.parse(HTTPI.post("http://app/route/api/action", invalid_request).body)
688
+ expect(response_hash["soap:Envelope"]["soap:Body"]["soap:Fault"]['faultstring']).to eq "Invalid SOAP request"
689
+ end
690
+ end
691
+
692
+ describe "WS Security" do
693
+ it "appends username_token to params" do
694
+ mock_controller(wsse_username: "gorilla", wsse_password: "secret") do
695
+ soap_action "checkToken", :args => :integer, :return => nil, :to => 'check_token'
696
+ def check_token
697
+ expect(request.env['WSSE_TOKEN']['username']).to eq "gorilla"
698
+ expect(request.env['WSSE_TOKEN']['password']).to eq "secret"
699
+ render :soap => nil
700
+ end
701
+ end
702
+
703
+ savon(:check_token, 42) do
704
+ wsse_auth "gorilla", "secret"
705
+ end
706
+ end
707
+
708
+ it "handles PasswordText auth" do
709
+ mock_controller(wsse_username: "gorilla", wsse_password: "secret") do
710
+ soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth'
711
+ def check_auth
712
+ render :soap => (params[:value] == 42)
713
+ end
714
+ end
715
+
716
+ # correct auth
717
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "secret" } }.
718
+ not_to raise_exception
719
+
720
+ # wrong user
721
+ expect { savon(:check_auth, 42){ wsse_auth "chimpanzee", "secret" } }.
722
+ to raise_exception(Savon::SOAPFault)
723
+
724
+ # wrong pass
725
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "nicetry" } }.
726
+ to raise_exception(Savon::SOAPFault)
727
+
728
+ # no auth
729
+ expect { savon(:check_auth, 42) }.
730
+ to raise_exception(Savon::SOAPFault)
731
+ end
732
+
733
+ it "handles PasswordDigest auth" do
734
+ mock_controller(wsse_username: "gorilla", wsse_password: "secret") do
735
+ soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth'
736
+ def check_auth
737
+ render :soap => (params[:value] == 42)
738
+ end
739
+ end
740
+
741
+ # correct auth
742
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "secret" } }.
743
+ not_to raise_exception
744
+
745
+ # correct digest auth
746
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "secret", :digest } }.
747
+ not_to raise_exception
748
+
749
+ # wrong user
750
+ expect { savon(:check_auth, 42){ wsse_auth "chimpanzee", "secret", :digest } }.
751
+ to raise_exception(Savon::SOAPFault)
752
+
753
+ # wrong pass
754
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "nicetry", :digest } }.
755
+ to raise_exception(Savon::SOAPFault)
756
+
757
+ # no auth
758
+ expect { savon(:check_auth, 42) }.
759
+ to raise_exception(Savon::SOAPFault)
760
+ end
761
+
762
+ it "handles auth callback" do
763
+ mock_controller(
764
+ wsse_auth_callback: lambda {|user, password|
765
+ return user == "gorilla" && password == "secret"
766
+ }
767
+ ) do
768
+ soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth'
769
+ def check_auth
770
+ render :soap => (params[:value] == 42)
771
+ end
772
+ end
773
+
774
+ # correct auth
775
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "secret" } }.
776
+ not_to raise_exception
777
+
778
+ # correct digest auth
779
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "secret", :digest } }.
780
+ to raise_exception(Savon::SOAPFault)
781
+
782
+ # wrong user
783
+ expect { savon(:check_auth, 42){ wsse_auth "chimpanzee", "secret", :digest } }.
784
+ to raise_exception(Savon::SOAPFault)
785
+
786
+ # wrong pass
787
+ expect { savon(:check_auth, 42){ wsse_auth "gorilla", "nicetry", :digest } }.
788
+ to raise_exception(Savon::SOAPFault)
789
+
790
+ # no auth
791
+ expect { savon(:check_auth, 42) }.
792
+ to raise_exception(Savon::SOAPFault)
793
+ end
794
+
795
+ end
796
+
797
+ end