weasel_diesel 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +23 -0
- data/README.md +231 -0
- data/Rakefile +12 -0
- data/lib/documentation.rb +151 -0
- data/lib/framework_ext/sinatra.rb +30 -0
- data/lib/framework_ext/sinatra_controller.rb +80 -0
- data/lib/inflection.rb +460 -0
- data/lib/json_response_verification.rb +109 -0
- data/lib/params.rb +374 -0
- data/lib/params_verification.rb +268 -0
- data/lib/response.rb +457 -0
- data/lib/weasel_diesel.rb +415 -0
- data/lib/weasel_diesel/version.rb +3 -0
- data/lib/ws_list.rb +58 -0
- data/spec/hello_world_controller.rb +5 -0
- data/spec/hello_world_service.rb +20 -0
- data/spec/json_response_description_spec.rb +124 -0
- data/spec/json_response_verification_spec.rb +225 -0
- data/spec/params_verification_spec.rb +102 -0
- data/spec/preferences_service.rb +10 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/test_services.rb +102 -0
- data/spec/wsdsl_sinatra_ext_spec.rb +26 -0
- data/spec/wsdsl_spec.rb +314 -0
- data/weasel_diesel.gemspec +30 -0
- metadata +127 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
describe_service "hello_world.xml" do |service|
|
2
|
+
service.formats :xml
|
3
|
+
service.http_verb :get
|
4
|
+
service.disable_auth # on by default
|
5
|
+
|
6
|
+
service.param.string :name, :default => 'World'
|
7
|
+
|
8
|
+
service.response do |response|
|
9
|
+
response.element(:name => "greeting") do |e|
|
10
|
+
e.attribute "message" => :string, :doc => "The greeting message sent back."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
service.documentation do |doc|
|
15
|
+
doc.overall "This service provides a simple hello world implementation example."
|
16
|
+
doc.params :name, "The name of the person to greet."
|
17
|
+
doc.example "<code>http://ps3.yourgame.com/hello_world.xml?name=Matt</code>"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe "WeaselDiesel JSON response description" do
|
4
|
+
|
5
|
+
# JSON response example
|
6
|
+
=begin
|
7
|
+
{ vouchers: [
|
8
|
+
{
|
9
|
+
id : 1,
|
10
|
+
redeemed : false,
|
11
|
+
created_at : 123123123123,
|
12
|
+
option: {
|
13
|
+
id : 1231,
|
14
|
+
price: 123.32
|
15
|
+
}
|
16
|
+
},
|
17
|
+
{
|
18
|
+
id : 2,
|
19
|
+
redeemed : true,
|
20
|
+
created_at : 123123123123,
|
21
|
+
option: {
|
22
|
+
id : 1233,
|
23
|
+
price: 1.32
|
24
|
+
}
|
25
|
+
},
|
26
|
+
] }
|
27
|
+
=end
|
28
|
+
|
29
|
+
before :all do
|
30
|
+
@timestamp = Time.now.to_i
|
31
|
+
@service = describe_service "json_list" do |service|
|
32
|
+
service.formats :json
|
33
|
+
service.response do |response|
|
34
|
+
response.array :vouchers do |node|
|
35
|
+
node.key :id
|
36
|
+
node.type :Voucher
|
37
|
+
node.string :name, :mock => "test"
|
38
|
+
node.integer :id, :doc => "Identifier"
|
39
|
+
node.boolean :redeemed
|
40
|
+
node.datetime :created_at, :mock => @timestamp
|
41
|
+
node.object :option do |option|
|
42
|
+
option.integer :id
|
43
|
+
option.integer :deal_id, :mock => 1
|
44
|
+
option.float :price
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@response = @service.response
|
50
|
+
@root_node = @response.nodes.find{|n| n.name == :vouchers}
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should handle the json root node" do
|
55
|
+
@root_node.should_not be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should handle a node property list" do
|
59
|
+
props = @root_node.properties
|
60
|
+
props.should_not be_empty
|
61
|
+
{:id => :integer, :redeemed => :boolean, :created_at => :datetime}.each do |key, type|
|
62
|
+
prop = props.find{|prop| prop.name == key}
|
63
|
+
prop.should_not be_nil
|
64
|
+
prop.type.should == type
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should handle a nested object with properties" do
|
69
|
+
@root_node.objects.should_not be_nil
|
70
|
+
option = @root_node.objects.find{|o| o.name == :option}
|
71
|
+
option.should_not be_nil
|
72
|
+
{:id => :integer, :deal_id => :integer, :price => :float}.each do |key, type|
|
73
|
+
prop = option.properties.find{|prop| prop.name == key}
|
74
|
+
if prop.nil?
|
75
|
+
puts option.properties.inspect
|
76
|
+
puts [key, type].inspect
|
77
|
+
end
|
78
|
+
prop.should_not be_nil
|
79
|
+
prop.type.should == type
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should allow some meta attributes" do
|
84
|
+
atts = @root_node.meta_attributes
|
85
|
+
atts.should_not be_nil
|
86
|
+
{:key => :id, :type => :Voucher}.each do |type, value|
|
87
|
+
meta = atts.find{|att| att.type == type}
|
88
|
+
puts [type, atts].inspect if meta.nil?
|
89
|
+
meta.should_not be_nil
|
90
|
+
meta.value.should == value
|
91
|
+
end
|
92
|
+
@root_node.key.should == :id
|
93
|
+
@root_node.type.should == :Voucher
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should handle mocked values properly" do
|
97
|
+
created_at = @root_node.properties.find{|prop| prop.name == :created_at}
|
98
|
+
created_at.opts[:mock].should == @timestamp
|
99
|
+
option = @root_node.objects.find{|prop| prop.name == :option}
|
100
|
+
deal_id = option.properties.find{|prop| prop.name == :deal_id}
|
101
|
+
deal_id.opts[:mock].should == 1
|
102
|
+
name = @root_node.properties.find{|prop| prop.name == :name}
|
103
|
+
name.opts[:mock].should == "test"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should allow an anonymous object at the root of the response" do
|
107
|
+
service = describe_service "json_anonymous_obj" do |service|
|
108
|
+
service.formats :json
|
109
|
+
service.response do |response|
|
110
|
+
response.object do |obj|
|
111
|
+
obj.integer :id
|
112
|
+
obj.string :foo
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
response = service.response
|
117
|
+
response.nodes.should_not be_empty
|
118
|
+
obj = response.nodes.first
|
119
|
+
obj.should_not be_nil
|
120
|
+
obj.properties.find{|prop| prop.name == :id}.should_not be_nil
|
121
|
+
obj.properties.find{|prop| prop.name == :foo}.should_not be_nil
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
require File.expand_path("../lib/json_response_verification", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
WeaselDiesel.send(:include, JSONResponseVerification)
|
5
|
+
|
6
|
+
describe "JSON response verification" do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@service = describe_service "json_response_verification" do |service|
|
10
|
+
service.response do |response|
|
11
|
+
response.element(:name => :user) do |user|
|
12
|
+
user.integer :id
|
13
|
+
user.string :name
|
14
|
+
user.datetime :created_at
|
15
|
+
user.object :creds do |creds|
|
16
|
+
creds.integer :id
|
17
|
+
creds.float :price
|
18
|
+
creds.boolean :enabled
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@second_service = describe_service "anonym_obj_json_response_verification" do |service|
|
25
|
+
service.response do |response|
|
26
|
+
response.object do |user|
|
27
|
+
user.integer :id
|
28
|
+
user.string :name, :null => true
|
29
|
+
user.datetime :created_at
|
30
|
+
user.object :creds do |creds|
|
31
|
+
creds.integer :id
|
32
|
+
creds.float :price
|
33
|
+
creds.boolean :enabled
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
@third_service = describe_service "with_array" do |service|
|
40
|
+
service.response do |response|
|
41
|
+
response.array :users do |node|
|
42
|
+
node.integer :id
|
43
|
+
node.string :name
|
44
|
+
node.boolean :admin, :doc => "true if the user is an admin"
|
45
|
+
node.string :state, :doc => "test", :null => true
|
46
|
+
node.datetime :last_login_at
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@forth_service = describe_service "with_nested_array" do |service|
|
52
|
+
service.response do |response|
|
53
|
+
response.array :users do |node|
|
54
|
+
node.integer :id
|
55
|
+
node.string :name
|
56
|
+
node.boolean :admin, :doc => "true if the user is an admin"
|
57
|
+
node.string :state, :doc => "test"
|
58
|
+
node.array :pets do |pet|
|
59
|
+
pet.integer :id
|
60
|
+
pet.string :name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@optional_prop_service = describe_service "opt_prop_service" do |service|
|
67
|
+
service.response do |response|
|
68
|
+
response.object do |obj|
|
69
|
+
obj.string :email, :null => true
|
70
|
+
obj.integer :city_id, :null => true
|
71
|
+
obj.string :full_name, :null => true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def valid_response(namespaced=true)
|
80
|
+
response = {
|
81
|
+
"id" => 1,
|
82
|
+
"name" => "matt",
|
83
|
+
"created_at" => "2011-09-22T16:32:46-07:00",
|
84
|
+
"creds" => { "id" => 42, "price" => 2010.07, "enabled" => false }
|
85
|
+
}
|
86
|
+
namespaced ? {"user" => response} : response
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_array_response
|
90
|
+
{"users" => [
|
91
|
+
{"id" => 1,
|
92
|
+
"admin" => true,
|
93
|
+
"name" => "Heidi",
|
94
|
+
"state" => "retired",
|
95
|
+
"last_login_at" => "2011-09-22T22:46:35-07:00"
|
96
|
+
},
|
97
|
+
{"id" => 2,
|
98
|
+
"admin" => false,
|
99
|
+
"name" => "Giana",
|
100
|
+
"state" => "playing",
|
101
|
+
"last_login_at" => "2011-09-22T22:46:35-07:00"
|
102
|
+
}]
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def valid_nested_array_response
|
107
|
+
{"users" => [
|
108
|
+
{"id" => 1,
|
109
|
+
"admin" => true,
|
110
|
+
"name" => "Heidi",
|
111
|
+
"state" => "retired",
|
112
|
+
"pets" => []
|
113
|
+
},
|
114
|
+
{"id" => 2,
|
115
|
+
"admin" => false,
|
116
|
+
"name" => "Giana",
|
117
|
+
"state" => "playing",
|
118
|
+
"pets" => [{"id" => 23, "name" => "medor"}, {"id" => 34, "name" => "rex"}]
|
119
|
+
}]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
it "should validate the response" do
|
125
|
+
valid, errors = @service.validate_hash_response(valid_response)
|
126
|
+
errors.should == []
|
127
|
+
valid.should be_true
|
128
|
+
errors.should be_empty
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should detect that the response is missing the top level object" do
|
132
|
+
response = valid_response
|
133
|
+
response.delete("user")
|
134
|
+
valid, errors = @service.validate_hash_response(response)
|
135
|
+
valid.should be_false
|
136
|
+
errors.should_not be_empty
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should detect that a property integer type is wrong" do
|
140
|
+
response = valid_response
|
141
|
+
response["user"]["id"] = 'test'
|
142
|
+
valid, errors = @service.validate_hash_response(response)
|
143
|
+
valid.should be_false
|
144
|
+
errors.should_not be_empty
|
145
|
+
errors.first.should match(/id/)
|
146
|
+
errors.first.should match(/wrong type/)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should detect that an integer attribute value is nil" do
|
150
|
+
response = valid_response
|
151
|
+
response["user"]["id"] = nil
|
152
|
+
valid, errors = @service.validate_hash_response(response)
|
153
|
+
valid.should be_false
|
154
|
+
errors.should_not be_empty
|
155
|
+
errors.first.should match(/id/)
|
156
|
+
errors.first.should match(/wrong type/)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should detect that a string attribute value is nil [bug]" do
|
160
|
+
response = valid_response
|
161
|
+
response["user"]["name"] = nil
|
162
|
+
valid, errors = @service.validate_hash_response(response)
|
163
|
+
valid.should be_false
|
164
|
+
errors.should_not be_empty
|
165
|
+
errors.first.should match(/name/)
|
166
|
+
errors.first.should match(/wrong type/)
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
it "should detect that a nested object is missing" do
|
171
|
+
response = valid_response
|
172
|
+
response["user"].delete("creds")
|
173
|
+
valid, errors = @service.validate_hash_response(response)
|
174
|
+
valid.should be_false
|
175
|
+
errors.first.should match(/creds/)
|
176
|
+
errors.first.should match(/missing/)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should validate non namespaced responses" do
|
180
|
+
valid, errors = @second_service.validate_hash_response(valid_response(false))
|
181
|
+
valid.should be_true
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should validate nil attributes if marked as nullable" do
|
185
|
+
response = valid_response(false)
|
186
|
+
response["name"] = nil
|
187
|
+
valid, errors = @second_service.validate_hash_response(response)
|
188
|
+
valid.should be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
it "should validate array items" do
|
193
|
+
valid, errors = @third_service.validate_hash_response(valid_array_response)
|
194
|
+
valid.should be_true
|
195
|
+
errors.should be_empty
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should validate an empty array" do
|
199
|
+
response = valid_array_response
|
200
|
+
response["users"] = []
|
201
|
+
valid, errors = @third_service.validate_hash_response(response)
|
202
|
+
valid.should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should catch error in an array item" do
|
206
|
+
response = valid_array_response
|
207
|
+
response["users"][1]["id"] = 'test'
|
208
|
+
valid, errors = @third_service.validate_hash_response(response)
|
209
|
+
valid.should be_false
|
210
|
+
errors.should_not be_empty
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should validate nested arrays" do
|
214
|
+
valid, errors = @forth_service.validate_hash_response(valid_nested_array_response)
|
215
|
+
valid.should be_true
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
it "should respect optional properties" do
|
220
|
+
valid, errors = @optional_prop_service.validate_hash_response({})
|
221
|
+
valid.should be_true
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe ParamsVerification do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
@service = WSList.all.find{|s| s.url == 'services/test.xml'}
|
7
|
+
@service.should_not be_nil
|
8
|
+
@valid_params = {'framework' => 'RSpec', 'version' => '1.02', 'user' => {'id' => '123'}}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should validate valid params" do
|
12
|
+
params = @valid_params.dup
|
13
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should_not raise_exception
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return the params" do
|
17
|
+
params = @valid_params.dup
|
18
|
+
returned_params = ParamsVerification.validate!(params, @service.defined_params)
|
19
|
+
returned_params.should be_an_instance_of(Hash)
|
20
|
+
returned_params.keys.size.should >= 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should set the default value for an optional param" do
|
24
|
+
params = @valid_params.dup
|
25
|
+
params['timestamp'].should be_nil
|
26
|
+
returned_params = ParamsVerification.validate!(params, @service.defined_params)
|
27
|
+
returned_params['timestamp'].should_not be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set the default value for a namespace optional param" do
|
31
|
+
params = {'framework' => 'RSpec', 'version' => '1.02', 'user' => {'id' => '123'}}
|
32
|
+
params['user']['mailing_list'].should be_nil
|
33
|
+
returned_params = ParamsVerification.validate!(params, @service.defined_params)
|
34
|
+
returned_params['user']['mailing_list'].should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise an exception when a required param is missing" do
|
38
|
+
params = @valid_params.dup
|
39
|
+
params.delete('framework')
|
40
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::MissingParam)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should cast a comma delimited string into an array when param marked as an array" do
|
44
|
+
service = WSList.all.find{|s| s.url == "services/array_param.xml"}
|
45
|
+
service.should_not be_nil
|
46
|
+
params = {'seq' => "a,b,c,d,e,g"}
|
47
|
+
validated = ParamsVerification.validate!(params, service.defined_params)
|
48
|
+
validated['seq'].should == %W{a b c d e g}
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should not raise an exception if a req array param doesn't contain a comma" do
|
52
|
+
service = WSList.all.find{|s| s.url == "services/array_param.xml"}
|
53
|
+
params = {'seq' => "a b c d e g"}
|
54
|
+
lambda{ ParamsVerification.validate!(params, service.defined_params) }.should_not raise_exception(ParamsVerification::InvalidParamType)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should raise an exception when a param is of the wrong type" do
|
58
|
+
params = @valid_params.dup
|
59
|
+
params['user']['id'] = 'abc'
|
60
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should raise an exception when a param is under the minvalue" do
|
64
|
+
params = @valid_params.dup
|
65
|
+
params['num'] = 1
|
66
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should raise an exception when a param isn't in the param option list" do
|
70
|
+
params = @valid_params.dup
|
71
|
+
params['alpha'] = 'z'
|
72
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should raise an exception when a nested optional param isn't in the param option list" do
|
76
|
+
params = @valid_params.dup
|
77
|
+
params['user']['sex'] = 'large'
|
78
|
+
lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
|
79
|
+
# other service
|
80
|
+
params = {'preference' => {'region_code' => 'us', 'language_code' => 'de'}}
|
81
|
+
service = WSList.all.find{|s| s.url == 'preferences.xml'}
|
82
|
+
service.should_not be_nil
|
83
|
+
lambda{ ParamsVerification.validate!(params, service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should raise an exception when a required param is present but doesn't match the limited set of options" do
|
87
|
+
service = describe_service "search" do |service|
|
88
|
+
service.params { |p| p.string :by, :options => ['name', 'code', 'last_four'], :required => true }
|
89
|
+
end
|
90
|
+
params = {'by' => 'foo'}
|
91
|
+
lambda{ ParamsVerification.validate!(params, service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
it "should validate that no params are passed when accept_no_params! is set on a service" do
|
96
|
+
service = WSList.all.find{|s| s.url == "services/test_no_params.xml"}
|
97
|
+
service.should_not be_nil
|
98
|
+
params = @valid_params.dup
|
99
|
+
lambda{ ParamsVerification.validate!(params, service.defined_params) }.should raise_exception
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|