weasel_diesel 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|