nogara-wash_out 0.5.2

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 (59) hide show
  1. data/.gitignore +9 -0
  2. data/.travis.yml +3 -0
  3. data/Appraisals +7 -0
  4. data/CHANGELOG.md +79 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.lock +118 -0
  7. data/LICENSE +22 -0
  8. data/README.md +181 -0
  9. data/Rakefile +14 -0
  10. data/app/helpers/wash_out_helper.rb +64 -0
  11. data/app/views/wash_with_soap/error.builder +10 -0
  12. data/app/views/wash_with_soap/response.builder +13 -0
  13. data/app/views/wash_with_soap/wsdl.builder +68 -0
  14. data/gemfiles/rails-3.0.11.gemfile +8 -0
  15. data/gemfiles/rails-3.0.11.gemfile.lock +130 -0
  16. data/gemfiles/rails-3.1.3.gemfile +8 -0
  17. data/gemfiles/rails-3.1.3.gemfile.lock +141 -0
  18. data/init.rb +1 -0
  19. data/lib/wash_out.rb +35 -0
  20. data/lib/wash_out/dispatcher.rb +206 -0
  21. data/lib/wash_out/engine.rb +30 -0
  22. data/lib/wash_out/model.rb +25 -0
  23. data/lib/wash_out/param.rb +176 -0
  24. data/lib/wash_out/router.rb +36 -0
  25. data/lib/wash_out/soap.rb +40 -0
  26. data/lib/wash_out/type.rb +28 -0
  27. data/lib/wash_out/version.rb +3 -0
  28. data/lib/wash_out/wsse.rb +79 -0
  29. data/spec/dummy/Rakefile +7 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  31. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  32. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/spec/dummy/config.ru +4 -0
  34. data/spec/dummy/config/application.rb +42 -0
  35. data/spec/dummy/config/boot.rb +10 -0
  36. data/spec/dummy/config/environment.rb +5 -0
  37. data/spec/dummy/config/environments/development.rb +23 -0
  38. data/spec/dummy/config/environments/test.rb +30 -0
  39. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/dummy/config/initializers/inflections.rb +10 -0
  41. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  42. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  43. data/spec/dummy/config/initializers/session_store.rb +8 -0
  44. data/spec/dummy/config/locales/en.yml +5 -0
  45. data/spec/dummy/config/routes.rb +58 -0
  46. data/spec/dummy/public/404.html +26 -0
  47. data/spec/dummy/public/422.html +26 -0
  48. data/spec/dummy/public/500.html +26 -0
  49. data/spec/dummy/public/favicon.ico +0 -0
  50. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  51. data/spec/dummy/script/rails +6 -0
  52. data/spec/spec_helper.rb +51 -0
  53. data/spec/support/httpi-rack.rb +46 -0
  54. data/spec/wash_out/dispatcher_spec.rb +65 -0
  55. data/spec/wash_out/param_spec.rb +26 -0
  56. data/spec/wash_out/type_spec.rb +23 -0
  57. data/spec/wash_out_spec.rb +686 -0
  58. data/wash_out.gemspec +21 -0
  59. metadata +183 -0
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
24
+ </div>
25
+ </body>
26
+ </html>
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,51 @@
1
+ # Configure Rails Envinronment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
+ require "rails/test_help"
6
+ require "rspec/rails"
7
+
8
+ Rails.backtrace_cleaner.remove_silencers!
9
+
10
+ # Load support files
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
12
+
13
+ RSpec.configure do |config|
14
+ # Remove this line if you don't want RSpec's should and should_not
15
+ # methods or matchers
16
+ require 'rspec/expectations'
17
+ config.include RSpec::Matchers
18
+
19
+ # == Mock Framework
20
+ config.mock_with :rspec
21
+ end
22
+
23
+ require 'savon'
24
+
25
+ Savon.configure do |config|
26
+ config.log = false # disable logging
27
+ end
28
+
29
+ HTTPI.logger = Logger.new(open("/dev/null", 'w'))
30
+ HTTPI.adapter = :rack
31
+
32
+ HTTPI::Adapters::Rack.mount 'app', Dummy::Application
33
+
34
+ Dummy::Application.routes.draw do
35
+ wash_out :api
36
+ end
37
+
38
+ def client
39
+ Savon::Client.new do
40
+ wsdl.document = 'http://app/api/wsdl'
41
+ end
42
+ end
43
+
44
+ def mock_controller(&block)
45
+ Object.send :remove_const, :ApiController if defined?(ApiController)
46
+ Object.send :const_set, :ApiController, Class.new(ApplicationController) {
47
+ include WashOut::SOAP
48
+
49
+ class_exec &block if block
50
+ }
51
+ end
@@ -0,0 +1,46 @@
1
+ require 'httpi'
2
+ require 'httpi/response'
3
+
4
+ module HTTPI
5
+ module Adapters
6
+ # This is an adapter for testing the Rack applications with HTTPI-capable
7
+ # clients.
8
+ class Rack
9
+ class << self
10
+ attr_accessor :mounted_apps
11
+ end
12
+
13
+ self.mounted_apps = {}
14
+
15
+ def self.mount(host, application)
16
+ self.mounted_apps[host] = application
17
+ end
18
+
19
+ def initialize(request=nil)
20
+ end
21
+
22
+ def method_missing(method, *args)
23
+ if %w{get post head put delete}.include?(method.to_s)
24
+ request, = args
25
+
26
+ app = self.class.mounted_apps[request.url.host]
27
+ mock_req = ::Rack::MockRequest.new(app)
28
+
29
+ env = {}
30
+ request.headers.each do |header, value|
31
+ env["HTTP_#{header.gsub('-', '_').upcase}"] = value
32
+ end
33
+
34
+ mock_resp = mock_req.request(method.to_s.upcase, request.url.to_s,
35
+ { :fatal => true, :input => request.body.to_s }.merge(env))
36
+
37
+ HTTPI::Response.new(mock_resp.status, mock_resp.headers, mock_resp.body)
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Adapter::ADAPTERS[:rack] = { :class => Adapters::Rack, :require => 'rack/mock' }
46
+ end
@@ -0,0 +1,65 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOut::Dispatcher do
6
+
7
+ class TestBody
8
+ attr_accessor :read
9
+ def initialize(read); @read = read; end
10
+ end
11
+
12
+ class TestRequest
13
+ attr_accessor :body
14
+ def initialize(body); @body = body; end
15
+ end
16
+
17
+ class Dispatcher < ApplicationController
18
+ include WashOut::SOAP
19
+
20
+ def self.mock(text="")
21
+ dispatcher = self.new
22
+ dispatcher.request = TestRequest.new(TestBody.new(text))
23
+ dispatcher
24
+ end
25
+
26
+ def params
27
+ @_params
28
+ end
29
+ end
30
+
31
+ it "finds nested hashes" do
32
+ WashOut::Dispatcher.deep_select(:foo => 1){|k,v| k == :foo}.should == [1]
33
+ WashOut::Dispatcher.deep_select({:foo => {:foo => 1}}){|k,v| k == :foo}.should == [{:foo => 1}, 1]
34
+ end
35
+
36
+ it "replaces nested hashed" do
37
+ WashOut::Dispatcher.deep_replace_href({:foo => {:@href => 1}}, {1 => 2}).should == {:foo => 2}
38
+ WashOut::Dispatcher.deep_replace_href({:bar => {:foo => {:@href => 1}}}, {1 => 2}).should == {:bar => {:foo => 2}}
39
+ end
40
+
41
+ it "parses typical request" do
42
+ dispatcher = Dispatcher.mock("<foo>1</foo>")
43
+ dispatcher._parse_soap_parameters
44
+ dispatcher.params.should == {:foo => "1"}
45
+ end
46
+
47
+ it "parses href request" do
48
+ dispatcher = Dispatcher.mock <<-XML
49
+ <request>
50
+ <entities href="#id1">
51
+ </entities>
52
+ </request>
53
+ <entity id="id1">
54
+ <foo><bar>1</bar></foo>
55
+ <sub href="#id2" />
56
+ </entity>
57
+ <ololo id="id2">
58
+ <foo>1</foo>
59
+ </ololo>
60
+ XML
61
+ dispatcher._parse_soap_parameters
62
+ dispatcher.params[:request][:entities].should == {:foo=>{:bar=>"1"}, :sub=>{:foo=>"1", :@id=>"id2"}, :@id=>"id1"}
63
+ end
64
+
65
+ end
@@ -0,0 +1,26 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOut::Param do
6
+
7
+ it "loads custom_types" do
8
+ class Abraka1 < WashOut::Type
9
+ map(
10
+ :test => :string
11
+ )
12
+ end
13
+ class Abraka2 < WashOut::Type
14
+ type_name 'test'
15
+ map :foo => Abraka1
16
+ end
17
+
18
+ map = WashOut::Param.parse_def Abraka2
19
+
20
+ map.should be_a_kind_of(Array)
21
+ map[0].name.should == 'Value'
22
+ map[0].map[0].name.should == 'Foo'
23
+ map[0].map[0].map[0].name.should == 'Test'
24
+ end
25
+
26
+ end
@@ -0,0 +1,23 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOut::Type do
6
+
7
+ it "defines custom type" do
8
+ class Abraka1 < WashOut::Type
9
+ map :test => :string
10
+ end
11
+ class Abraka2 < WashOut::Type
12
+ type_name 'test'
13
+ map :foo => Abraka1
14
+ end
15
+
16
+ Abraka1.wash_out_param_name.should == 'Abraka1'
17
+ Abraka1.wash_out_param_map.should == {:test => :string}
18
+
19
+ Abraka2.wash_out_param_name.should == 'Test'
20
+ Abraka2.wash_out_param_map.should == {:foo => Abraka1}
21
+ end
22
+
23
+ end
@@ -0,0 +1,686 @@
1
+ #encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WashOut do
6
+ before(:each) do
7
+ WashOut::Engine.snakecase_input = true
8
+ WashOut::Engine.camelize_wsdl = true
9
+ WashOut::Engine.namespace = false
10
+ end
11
+
12
+ it "should be valid" do
13
+ WashOut.should be_a(Module)
14
+ end
15
+
16
+ it "should allow to include SOAP module" do
17
+ lambda {
18
+ mock_controller do
19
+ # nothing
20
+ end
21
+ }.should_not raise_exception
22
+ end
23
+
24
+ it "should generate WSDL" do
25
+ mock_controller do
26
+ soap_action :result, :args => nil, :return => :int
27
+ def answer
28
+ render :soap => "42"
29
+ end
30
+
31
+ soap_action "getArea", :args => { :circle => { :center => { :x => [:integer],
32
+ :y => :integer },
33
+ :radius => :double } },
34
+ :return => { :area => :double,
35
+ :distance_from_o => :double },
36
+ :to => :get_area
37
+ def get_area
38
+ circle = params[:circle]
39
+ render :soap => { :area => Math::PI * circle[:radius] ** 2,
40
+ :distance_from_o => Math.sqrt(circle[:center][:x] ** 2 + circle[:center][:y] ** 2) }
41
+ end
42
+
43
+ soap_action "rocky", :args => { :circle1 => { :x => :integer } },
44
+ :return => { :circle2 => { :y => :integer } }
45
+ def rocky; end
46
+ end
47
+
48
+ xml = Nori.parse client.wsdl.xml
49
+
50
+ # Savon underscores method names so we
51
+ # get back just what we have at controller
52
+ client.wsdl.soap_actions.map{|x| x.to_s}.sort.should == [:result, :get_area, :rocky].map{|x| x.to_s}.sort
53
+
54
+ x = xml[:definitions][:types][:schema][:complex_type].find{|x| x[:'@name'] == 'Center'}[:sequence][:element].find{|x| x[:'@name'] == 'X'}
55
+ x[:'@min_occurs'].should == "0"
56
+ x[:'@max_occurs'].should == "unbounded"
57
+
58
+ xml[:definitions][:binding][:operation].map{|e| e[:'@name']}.sort.should == ['Result', 'getArea', 'rocky'].sort
59
+
60
+ client.wsdl.xml.include?('<xsd:complexType name="Circle1">').should == true
61
+ end
62
+
63
+ it "should allow definition of a simple action" do
64
+ lambda {
65
+ mock_controller do
66
+ soap_action "answer", :args => nil, :return => :integer
67
+ end
68
+ }.should_not raise_exception
69
+ end
70
+
71
+ it "should answer to request without parameters" do
72
+ mock_controller do
73
+ soap_action "answer", :args => nil, :return => :int
74
+ def answer
75
+ render :soap => "42"
76
+ end
77
+ end
78
+
79
+ client.request(:answer).to_hash[:answer_response][:value].should == "42"
80
+ end
81
+
82
+ it "should respond to request with insufficient parameters" do
83
+ mock_controller do
84
+ soap_action "answer", :args => {:a => :integer}, :return => :integer
85
+ def answer
86
+ render :soap => "42"
87
+ end
88
+ end
89
+
90
+ client.request(:answer).to_hash[:answer_response][:value].should == "42"
91
+ end
92
+
93
+ it "should answer to request with empty parameter" do
94
+ mock_controller do
95
+ soap_action "answer", :args => {:a => :string}, :return => {:a => :string}
96
+ def answer
97
+ render :soap => {:a => params[:a]}
98
+ end
99
+ end
100
+
101
+ client.request(:answer) do
102
+ soap.body = { :a => '' }
103
+ end.to_hash[:answer_response][:a].should == {:"@xsi:type"=>"xsd:string"}
104
+ end
105
+
106
+ it "should answer to request with one parameter" do
107
+ mock_controller do
108
+ soap_action "checkAnswer", :args => :integer, :return => :boolean, :to => 'check_answer'
109
+ def check_answer
110
+ render :soap => (params[:value] == 42)
111
+ end
112
+ end
113
+
114
+ client.request(:check_answer) do
115
+ soap.body = { :value => 42 }
116
+ end.to_hash[:check_answer_response][:value].should == true
117
+ client.request(:check_answer) do
118
+ soap.body = { :value => 13 }
119
+ end.to_hash[:check_answer_response][:value].should == false
120
+ end
121
+
122
+ it "handles incorrect requests" do
123
+ mock_controller do
124
+ soap_action "duty",
125
+ :args => {:bad => {:a => :string, :b => :string}, :good => {:a => :string, :b => :string}},
126
+ :return => nil
127
+ def duty
128
+ render :soap => nil
129
+ end
130
+ end
131
+
132
+ lambda {
133
+ client.request(:duty) do
134
+ soap.body = { :bad => 42, :good => nil }
135
+ end
136
+ }.should raise_exception(Savon::SOAP::Fault)
137
+ end
138
+
139
+ it "should handle snakecase option properly" do
140
+ WashOut::Engine.snakecase_input = false
141
+ WashOut::Engine.camelize_wsdl = false
142
+
143
+ mock_controller do
144
+ soap_action "rocknroll", :args => {:ZOMG => :string}, :return => nil
145
+ def rocknroll
146
+ params["ZOMG"].should == "yam!"
147
+ render :soap => nil
148
+ end
149
+ end
150
+
151
+ client.request(:rocknroll) do
152
+ soap.body = { "ZOMG" => 'yam!' }
153
+ end
154
+ end
155
+
156
+ context "optional arrays" do
157
+ it "should answer for simple structure" do
158
+ mock_controller do
159
+ soap_action "rocknroll",
160
+ :args => nil, :return => { :my_value => [:integer] }
161
+ def rocknroll
162
+ render :soap => {}
163
+ end
164
+ end
165
+
166
+ client.request(:rocknroll).to_hash[:rocknroll_response].should be_nil
167
+ end
168
+
169
+ it "should answer for complex structure" do
170
+ mock_controller do
171
+ soap_action "rocknroll",
172
+ :args => nil, :return => { :my_value => [{ :value => :integer}] }
173
+ def rocknroll
174
+ render :soap => {}
175
+ end
176
+ end
177
+
178
+ client.request(:rocknroll).to_hash[:rocknroll_response].should be_nil
179
+ end
180
+
181
+ it "should answer for nested complex structure" do
182
+ mock_controller do
183
+ soap_action "rocknroll",
184
+ :args => nil, :return => { :my_value => { :my_array => [{ :value => :integer}] } }
185
+ def rocknroll
186
+ render :soap => {}
187
+ end
188
+ end
189
+
190
+ client.request(:rocknroll).to_hash[:rocknroll_response][:my_value].should == { :"@xsi:type" => "tns:MyValue" }
191
+ end
192
+ end
193
+
194
+ it "should answer to request with two parameter" do
195
+ mock_controller do
196
+ soap_action "funky", :args => { :a => :integer, :b => :string }, :return => :string
197
+ def funky
198
+ render :soap => ((params[:a] * 10).to_s + params[:b])
199
+ end
200
+ end
201
+
202
+ client.request(:funky) do
203
+ soap.body = { :a => 42, :b => 'k' }
204
+ end.to_hash[:funky_response][:value].should == '420k'
205
+ end
206
+
207
+ it "should understand nested parameter specifications" do
208
+ mock_controller do
209
+ soap_action "getArea", :args => { :circle => { :center => { :x => :integer,
210
+ :y => :integer },
211
+ :radius => :double } },
212
+ :return => { :area => :double,
213
+ :distance_from_o => :double },
214
+ :to => :get_area
215
+ def get_area
216
+ circle = params[:circle]
217
+ render :soap => { :area => Math::PI * circle[:radius] ** 2,
218
+ :distance_from_o => Math.sqrt(circle[:center][:x] ** 2 + circle[:center][:y] ** 2) }
219
+ end
220
+ end
221
+
222
+ client.request(:get_area) do
223
+ soap.body = { :circle => { :center => { :x => 3, :y => 4 },
224
+ :radius => 5 } }
225
+ end.to_hash[:get_area_response].should == ({ :area => (Math::PI * 25).to_s, :distance_from_o => (5.0).to_s })
226
+ end
227
+
228
+ it "should allow arbitrary action names" do
229
+ name = 'AnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything'
230
+
231
+ mock_controller do
232
+ soap_action name,
233
+ :args => nil, :return => :integer, :to => :answer
234
+ def answer
235
+ render :soap => "forty two"
236
+ end
237
+ end
238
+
239
+ client.request(name).to_hash["#{name.underscore}_response".to_sym][:value].should == "forty two"
240
+ end
241
+
242
+ it "should correctly report SOAP errors" do
243
+ mock_controller do
244
+ soap_action "error", :args => { :need_error => :boolean }, :return => nil
245
+ def error
246
+ raise self.class.const_get(:SOAPError), "you wanted one" if params[:need_error]
247
+
248
+ render :soap => nil
249
+ end
250
+ end
251
+
252
+ lambda {
253
+ client.request(:error) do
254
+ soap.body = { :need_error => false }
255
+ end
256
+ }.should_not raise_exception
257
+ lambda {
258
+ client.request(:error) do
259
+ soap.body = { :need_error => true }
260
+ end
261
+ }.should raise_exception(Savon::SOAP::Fault)
262
+ end
263
+
264
+ it "should report a SOAP error if method does not exists" do
265
+ mock_controller
266
+
267
+ lambda {
268
+ client.request(:nonexistent)
269
+ }.should raise_exception(Savon::SOAP::Fault)
270
+ end
271
+
272
+ it "should be possible to explicitly render a SOAP error" do
273
+ mock_controller do
274
+ soap_action "error", :args => nil, :return => nil
275
+ def error
276
+ render_soap_error "a message"
277
+ end
278
+ end
279
+
280
+ lambda {
281
+ client.request(:error)
282
+ }.should raise_exception(Savon::SOAP::Fault)
283
+ end
284
+
285
+ it "should handle nested returns" do
286
+ mock_controller do
287
+ soap_action "gogogo",
288
+ :args => nil,
289
+ :return => {
290
+ :zoo => :string,
291
+ :boo => {
292
+ :moo => :string,
293
+ :doo => :string
294
+ }
295
+ }
296
+ def gogogo
297
+ render :soap => {
298
+ :zoo => 'zoo',
299
+ :boo => {
300
+ :moo => 'moo',
301
+ :doo => 'doo'
302
+ }
303
+ }
304
+ end
305
+ end
306
+
307
+ client.request(:gogogo)[:gogogo_response].should == {:zoo=>"zoo", :boo=>{:moo=>"moo", :doo=>"doo", :"@xsi:type"=>"tns:Boo"}}
308
+ end
309
+
310
+ it "should handle arrays" do
311
+ mock_controller do
312
+ soap_action "rumba",
313
+ :args => {
314
+ :rumbas => [:integer]
315
+ },
316
+ :return => nil
317
+ def rumba
318
+ params.should == {"rumbas" => [1, 2, 3]}
319
+ render :soap => nil
320
+ end
321
+ end
322
+
323
+ client.request(:rumba) do
324
+ soap.body = {
325
+ :rumbas => [1, 2, 3]
326
+ }
327
+ end
328
+ end
329
+
330
+ it "should handle complex structures inside arrays" do
331
+ mock_controller do
332
+ soap_action "rumba",
333
+ :args => {
334
+ :rumbas => [ {
335
+ :zombies => :string,
336
+ :puppies => :string
337
+ } ]
338
+ },
339
+ :return => nil
340
+ def rumba
341
+ params.should == {
342
+ "rumbas" => [
343
+ {"zombies" => 'suck', "puppies" => 'rock'},
344
+ {"zombies" => 'slow', "puppies" => 'fast'}
345
+ ]
346
+ }
347
+ render :soap => nil
348
+ end
349
+ end
350
+
351
+ client.request(:rumba) do
352
+ soap.body = {
353
+ :rumbas => [
354
+ {:zombies => 'suck', :puppies => 'rock'},
355
+ {:zombies => 'slow', :puppies => 'fast'}
356
+ ]
357
+ }
358
+ end
359
+ end
360
+
361
+ it "should be able to return arrays" do
362
+ mock_controller do
363
+ soap_action "rumba",
364
+ :args => nil,
365
+ :return => [:integer]
366
+ def rumba
367
+ render :soap => [1, 2, 3]
368
+ end
369
+ end
370
+
371
+ client.request(:rumba).to_hash[:rumba_response].should == {:value => ["1", "2", "3"]}
372
+ end
373
+
374
+ it "should deprecate old syntax" do
375
+ # save rspec context check
376
+ raise_runtime_exception = raise_exception(RuntimeError)
377
+
378
+ mock_controller do
379
+ lambda {
380
+ soap_action "rumba",
381
+ :args => :integer,
382
+ :return => []
383
+ }.should raise_runtime_exception
384
+ def rumba
385
+ render :soap => nil
386
+ end
387
+ end
388
+ end
389
+
390
+ it "should handle return of complex structures inside arrays" do
391
+ mock_controller do
392
+ soap_action "rumba",
393
+ :args => nil,
394
+ :return => {
395
+ :rumbas => [{:zombies => :string, :puppies => :string}]
396
+ }
397
+ def rumba
398
+ render :soap =>
399
+ {:rumbas => [
400
+ {:zombies => "suck1", :puppies => "rock1" },
401
+ {:zombies => "suck2", :puppies => "rock2" }
402
+ ]
403
+ }
404
+ end
405
+ end
406
+
407
+ client.request(:rumba)[:rumba_response].should == {
408
+ :rumbas => [
409
+ {:zombies => "suck1",:puppies => "rock1", :"@xsi:type"=>"tns:Rumbas"},
410
+ {:zombies => "suck2", :puppies => "rock2", :"@xsi:type"=>"tns:Rumbas" }
411
+ ]
412
+ }
413
+ end
414
+
415
+ it "should handle return of structs in structs in arrays" do
416
+ mock_controller do
417
+ soap_action "rumba",
418
+ :args => nil,
419
+ :return => [{:rumbas => {:zombies => :integer}}]
420
+
421
+ def rumba
422
+ render :soap => [{:rumbas => {:zombies => 100000}}, {:rumbas => {:zombies => 2}}]
423
+ end
424
+ end
425
+
426
+ client.request(:rumba)[:rumba_response].should == {
427
+ :value => [
428
+ {
429
+ :rumbas => {
430
+ :zombies => "100000",
431
+ :"@xsi:type" => "tns:Rumbas"
432
+ },
433
+ :"@xsi:type" => "tns:Value"
434
+ },
435
+ {
436
+ :rumbas => {
437
+ :zombies => "2",
438
+ :"@xsi:type" => "tns:Rumbas"
439
+ },
440
+ :"@xsi:type"=>"tns:Value"
441
+ }
442
+ ]
443
+ }
444
+ end
445
+
446
+ it "should handle complex structs/arrays" do
447
+ mock_controller do
448
+ soap_action "rumba",
449
+ :args => nil,
450
+ :return => {
451
+ :rumbas => [
452
+ {
453
+ :zombies => :string,
454
+ :puppies => [
455
+ {:kittens => :integer}
456
+ ]
457
+ }
458
+ ]
459
+ }
460
+
461
+ def rumba
462
+ render :soap => {
463
+ :rumbas => [
464
+ {
465
+ :zombies => "abc",
466
+ :puppies => [
467
+ {:kittens => 1},
468
+ {:kittens => 5},
469
+ ]
470
+ },
471
+ {
472
+ :zombies => "def",
473
+ :puppies => [
474
+ {:kittens => 4}
475
+ ]
476
+ }
477
+ ]
478
+ }
479
+ end
480
+ end
481
+
482
+ client.request(:rumba)[:rumba_response].should == {
483
+ :rumbas => [
484
+ {
485
+ :zombies => "abc",
486
+ :puppies => [
487
+ {
488
+ :kittens => "1",
489
+ :"@xsi:type" => "tns:Puppies"
490
+ },
491
+ {
492
+ :kittens => "5",
493
+ :"@xsi:type" => "tns:Puppies"
494
+ }
495
+ ],
496
+ :"@xsi:type"=>"tns:Rumbas"
497
+ },
498
+ {
499
+ :zombies => "def",
500
+ :puppies => {
501
+ :kittens => "4",
502
+ :"@xsi:type" => "tns:Puppies"
503
+ },
504
+ :"@xsi:type"=>"tns:Rumbas"
505
+ }
506
+ ]
507
+ }
508
+ end
509
+
510
+ it "handles dates" do
511
+ mock_controller do
512
+ soap_action "date",
513
+ :args => :date,
514
+ :return => :nil
515
+ def date
516
+ params[:value].should == Date.parse('2000-12-30') unless params[:value].blank?
517
+ render :soap => nil
518
+ end
519
+ end
520
+
521
+ client.request(:date) do
522
+ soap.body = {
523
+ :value => '2000-12-30'
524
+ }
525
+ end
526
+
527
+ lambda {
528
+ client.request(:date) do
529
+ soap.body = {
530
+ :value => nil
531
+ }
532
+ end
533
+ }.should_not raise_exception
534
+ end
535
+
536
+ describe "ws-security" do
537
+
538
+ it "should append username_token to params, if present" do
539
+ WashOut::Engine.wsse_username = nil
540
+ WashOut::Engine.wsse_password = nil
541
+
542
+ mock_controller do
543
+ soap_action "checkToken", :args => :integer, :return => nil, :to => 'check_token'
544
+ def check_token
545
+ request.env['WSSE_TOKEN']['username'].should == "gorilla"
546
+ request.env['WSSE_TOKEN']['password'].should == "secret"
547
+ render :soap => nil
548
+ end
549
+ end
550
+
551
+ client.request(:check_token) do
552
+ wsse.username = "gorilla"
553
+ wsse.password = "secret"
554
+ soap.body = { :value => 42 }
555
+ end
556
+ end
557
+
558
+ it "should handle PasswordText auth" do
559
+ WashOut::Engine.wsse_username = "gorilla"
560
+ WashOut::Engine.wsse_password = "secret"
561
+
562
+ mock_controller do
563
+ soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth'
564
+ def check_auth
565
+ render :soap => (params[:value] == 42)
566
+ end
567
+ end
568
+
569
+ # correct auth
570
+ lambda {
571
+ client.request(:check_auth) do
572
+ wsse.username = "gorilla"
573
+ wsse.password = "secret"
574
+ soap.body = { :value => 42 }
575
+ end
576
+ }.should_not raise_exception
577
+ # wrong user
578
+ lambda {
579
+ client.request(:check_auth) do
580
+ wsse.username = "chimpanzee"
581
+ wsse.password = "secret"
582
+ soap.body = { :value => 42 }
583
+ end
584
+ }.should raise_exception(Savon::SOAP::Fault)
585
+ # wrong pass
586
+ lambda {
587
+ client.request(:check_auth) do
588
+ wsse.username = "gorilla"
589
+ wsse.password = "nicetry"
590
+ soap.body = { :value => 42 }
591
+ end
592
+ }.should raise_exception(Savon::SOAP::Fault)
593
+ # no auth
594
+ lambda {
595
+ client.request(:check_auth) do
596
+ soap.body = { :value => 42 }
597
+ end
598
+ }.should raise_exception(Savon::SOAP::Fault)
599
+ end
600
+
601
+ it "should handle PasswordDigest auth" do
602
+ WashOut::Engine.wsse_username = "gorilla"
603
+ WashOut::Engine.wsse_password = "secret"
604
+
605
+ mock_controller do
606
+ soap_action "checkAuth", :args => :integer, :return => :boolean, :to => 'check_auth'
607
+ def check_auth
608
+ render :soap => (params[:value] == 42)
609
+ end
610
+ end
611
+
612
+ # correct auth
613
+ lambda {
614
+ client.request(:check_auth) do
615
+ wsse.credentials "gorilla", "secret", :digest
616
+ soap.body = { :value => 42 }
617
+ end
618
+ }.should_not raise_exception
619
+ # wrong user
620
+ lambda {
621
+ client.request(:check_auth) do
622
+ wsse.credentials "chimpanzee", "secret", :digest
623
+ soap.body = { :value => 42 }
624
+ end
625
+ }.should raise_exception(Savon::SOAP::Fault)
626
+ # wrong pass
627
+ lambda {
628
+ client.request(:check_auth) do
629
+ wsse.credentials "gorilla", "nicetry", :digest
630
+ soap.body = { :value => 42 }
631
+ end
632
+ }.should raise_exception(Savon::SOAP::Fault)
633
+ end
634
+
635
+ end
636
+
637
+ it 'will not let you pass an Array in the place of a Hash' do
638
+ mock_controller do
639
+ soap_action 'bad', :args => :integer, :return => {
640
+ :basic => :string,
641
+ :stallions => {
642
+ :stallion => [
643
+ :name => :string,
644
+ :wyldness => :integer,
645
+ ]
646
+ },
647
+ }
648
+ def bad
649
+ render :soap => {
650
+ :basic => 'hi',
651
+ :stallions => [{:name => 'ted', :wyldness => 11}]
652
+ }
653
+ end
654
+ end
655
+
656
+ lambda {
657
+ client.request(:bad).to_hash(:bad_response)
658
+ }.should raise_exception(
659
+ WashOut::Dispatcher::ProgrammerError,
660
+ /SOAP response .*wyldness.*Array.*Hash.*stallion/
661
+ )
662
+ end
663
+
664
+ it 'loudly fails if you return a string in place of an Array' do
665
+ mock_controller do
666
+ soap_action 'bad2', :args => :integer, :return => {
667
+ :basic => :string,
668
+ :telephone_booths => [:string]
669
+ }
670
+ def bad2
671
+ render :soap => {
672
+ :basic => 'hihi',
673
+ :telephone_booths => 'oops'
674
+ }
675
+ end
676
+ end
677
+
678
+ lambda {
679
+ client.request(:bad2).to_hash[:bad_response]
680
+ }.should raise_exception(
681
+ WashOut::Dispatcher::ProgrammerError,
682
+ /SOAP response .*oops.*String.*telephone_booths.*Array/
683
+ )
684
+ end
685
+
686
+ end