nogara-wash_out 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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