restfulness 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Restfulness::Dispatcher do
5
+
6
+ describe "#initialize" do
7
+ it "should assign app variable" do
8
+ obj = Restfulness::Dispatcher.new(:foo)
9
+ obj.app.should eql(:foo)
10
+ end
11
+ end
12
+
13
+
14
+ end
@@ -0,0 +1,34 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Restfulness::Dispatchers::Rack do
5
+
6
+ let :klass do
7
+ Restfulness::Dispatchers::Rack
8
+ end
9
+
10
+ let :app do
11
+ Class.new(Restfulness::Application).new
12
+ end
13
+
14
+ let :obj do
15
+ klass.new(app)
16
+ end
17
+
18
+ let :env do
19
+ {
20
+ 'REQUEST_METHOD' => 'GET',
21
+ 'SCRIPT_NAME' => 'projects',
22
+ 'PATH_INFO' => 'projects',
23
+ 'QUERY_STRING' => '',
24
+ 'SERVER_NAME' => 'localhost',
25
+ 'SERVER_PORT' => '3000',
26
+ 'HTTP_CONTENT_TYPE' => 'application/json'
27
+ }
28
+ end
29
+
30
+ describe "#" do
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::HTTPException do
4
+
5
+ describe "#initialize" do
6
+ it "should assign variables" do
7
+ obj = Restfulness::HTTPException.new(200, "payload", :message => 'foo', :headers => {})
8
+ obj.code.should eql(200)
9
+ obj.payload.should eql("payload")
10
+ obj.message.should eql('foo')
11
+ obj.headers.should eql({})
12
+ end
13
+
14
+ it "should use status code for message if none provided" do
15
+ obj = Restfulness::HTTPException.new(200, "payload")
16
+ obj.message.should eql('OK')
17
+ end
18
+ end
19
+
20
+
21
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::Path do
4
+
5
+ class PathResource < Restfulness::Resource
6
+ end
7
+
8
+ let :klass do
9
+ Restfulness::Path
10
+ end
11
+ let :route_class do
12
+ Restfulness::Route
13
+ end
14
+ let :resource_class do
15
+ PathResource
16
+ end
17
+ let :simple_route do
18
+ route_class.new('project', resource_class)
19
+ end
20
+ let :complex_route do
21
+ route_class.new('project', :project_id, 'status', resource_class)
22
+ end
23
+
24
+ describe "#initialize" do
25
+
26
+ it "should assign route" do
27
+ obj = klass.new(simple_route, '/project')
28
+ obj.route.should eql(simple_route)
29
+ end
30
+
31
+ context "simple paths" do
32
+ it "should prepare basic path" do
33
+ obj = klass.new(simple_route, '/project')
34
+ obj.components.should eql(['project'])
35
+ obj.params[:id].should be_nil
36
+ end
37
+
38
+ it "should prepare irregular path components" do
39
+ obj = klass.new(simple_route, '/project/')
40
+ obj.components.should eql(['project'])
41
+ end
42
+
43
+ it "should include id" do
44
+ obj = klass.new(simple_route, '/project/12345')
45
+ obj.components.should eql(['project', '12345'])
46
+ obj.params[:id].should eql('12345')
47
+ end
48
+ end
49
+
50
+
51
+ context "complex paths" do
52
+ it "should prepare path" do
53
+ obj = klass.new(complex_route, '/project/12345/status')
54
+ obj.components.should eql(['project', '12345', 'status'])
55
+ obj.params[:project_id].should eql('12345')
56
+ end
57
+
58
+ it "should handle end id" do
59
+ obj = klass.new(complex_route, '/project/12345/status/23456')
60
+ obj.components.should eql(['project', '12345', 'status', '23456'])
61
+ obj.params[:project_id].should eql('12345')
62
+ obj.params[:id].should eql('23456')
63
+ end
64
+ end
65
+ end
66
+
67
+ describe "#to_s" do
68
+ it "should provide simple string" do
69
+ obj = klass.new(complex_route, '/project/12345/status/23456')
70
+ obj.to_s.should eql('/project/12345/status/23456')
71
+ end
72
+ end
73
+
74
+ describe "#[]" do
75
+ let :obj do
76
+ obj = klass.new(complex_route, '/project/12345/status/23456')
77
+ end
78
+ it "should grant access to components by index" do
79
+ obj[0].should eql('project')
80
+ obj[1].should eql('12345')
81
+ obj[2].should eql('status')
82
+ obj[3].should eql('23456')
83
+ obj[4].should be_nil
84
+ end
85
+ it "should grant access to path parameters by symbol" do
86
+ obj[:project_id].should eql('12345')
87
+ obj[:id].should eql('23456')
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::Request do
4
+
5
+ let :app do
6
+ Class.new(Restfulness::Application) do
7
+ routes do
8
+ # nothing
9
+ end
10
+ end
11
+ end
12
+
13
+ let :klass do
14
+ Restfulness::Request
15
+ end
16
+
17
+ let :obj do
18
+ klass.new(app)
19
+ end
20
+
21
+ class RequestResource < Restfulness::Resource
22
+ end
23
+ let :resource do
24
+ RequestResource
25
+ end
26
+
27
+
28
+ describe "#initialize" do
29
+ it "should prepare basic objects" do
30
+ obj.action.should be_nil
31
+ obj.headers.should eql({})
32
+ obj.body.should be_nil
33
+ end
34
+ end
35
+
36
+ describe "#uri=" do
37
+ it "should convert URL into URI" do
38
+ obj.uri = "https://example.com/project/12345"
39
+ obj.uri.path.should eql("/project/12345")
40
+ end
41
+ end
42
+
43
+ describe "#path" do
44
+ it "should be nil if there is no route" do
45
+ obj.stub(:route).and_return(nil)
46
+ obj.path.should be_nil
47
+ end
48
+
49
+ context "with route" do
50
+ let :path do
51
+ obj.uri = "https://example.com/project/12345"
52
+ route = Restfulness::Route.new('project', resource)
53
+ obj.stub(:route).and_return(route)
54
+ obj.path
55
+ end
56
+
57
+ it "should build path" do
58
+ path.to_s.should eql('/project/12345')
59
+ end
60
+
61
+ it "should re-use same path object" do
62
+ path.object_id.should eql(obj.path.object_id)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe "#route" do
68
+ it "should ask the router for a route" do
69
+ obj.uri = "https://example.com/project/12345"
70
+ route = double(:Route)
71
+ app.router.should_receive(:route_for).with(obj.uri.path).and_return(route)
72
+ obj.route.should eql(route)
73
+ end
74
+ end
75
+
76
+ describe "#query" do
77
+ it "should parse uri with query" do
78
+ obj.uri = "https://example.com/project/12345?foo=bar&test=23"
79
+ obj.query.should be_a(HashWithIndifferentAccess)
80
+ obj.query[:foo].should eql('bar')
81
+ obj.query[:test].to_i.should eql(23)
82
+ end
83
+
84
+ it "should handle uri with empty query" do
85
+ obj.uri = "https://example.com/project/12345"
86
+ obj.query.should be_empty
87
+ end
88
+
89
+ it "should handle complex query items" do
90
+ obj.uri = "https://example.com/project/12345?foo[]=bar&foo[]=bar2&hash[a]=b&hash[b]=c"
91
+ obj.query[:foo].should eql(['bar', 'bar2'])
92
+ obj.query[:hash].should be_a(HashWithIndifferentAccess)
93
+ obj.query[:hash][:a].should eql('b')
94
+ end
95
+ end
96
+
97
+ describe "#params" do
98
+ it "should not return anything for empty body" do
99
+ obj.stub(:body).and_return(nil)
100
+ obj.params.should be_nil
101
+ end
102
+ it "should raise 406 error if no content type" do
103
+ obj.headers[:content_type] = nil
104
+ obj.body = "{\"foo\":\"bar\"}"
105
+ expect {
106
+ obj.params
107
+ }.to raise_error(Restfulness::HTTPException, "Not Acceptable")
108
+ end
109
+ it "should decode a JSON body" do
110
+ obj.headers[:content_type] = "application/json"
111
+ obj.body = "{\"foo\":\"bar\"}"
112
+ obj.params['foo'].should eql('bar')
113
+ end
114
+ end
115
+
116
+ describe "method helpers" do
117
+ it "should respond to method questions" do
118
+ [:get?, :post?, :put?, :delete?, :head?, :options?].each do |q|
119
+ obj.should respond_to(q)
120
+ end
121
+ end
122
+ it "should suggest type of method" do
123
+ obj.action = :get
124
+ obj.get?.should be_true
125
+ obj.post?.should be_false
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ describe Restfulness::Resource do
4
+
5
+ class GetResource < Restfulness::Resource
6
+ def get
7
+ 'result'
8
+ end
9
+ end
10
+
11
+ class GetPostResource < Restfulness::Resource
12
+ def get
13
+ 'result'
14
+ end
15
+ def post
16
+ 'post'
17
+ end
18
+ end
19
+
20
+ let :app do
21
+ Class.new(Restfulness::Application) do
22
+ routes do
23
+ # empty
24
+ end
25
+ end
26
+ end
27
+ let :request do
28
+ Restfulness::Request.new(app)
29
+ end
30
+ let :response do
31
+ Restfulness::Response.new(request)
32
+ end
33
+
34
+ describe "#initialize" do
35
+ let :resource do
36
+ GetResource
37
+ end
38
+ it "should assign request and response" do
39
+ obj = resource.new(request, response)
40
+ obj.request.should eql(request)
41
+ obj.response.should eql(response)
42
+ end
43
+ end
44
+
45
+ describe "#options" do
46
+ let :resource do
47
+ GetPostResource
48
+ end
49
+
50
+ it "should return list of supported methods" do
51
+ obj = resource.new(request, response)
52
+ obj.options.should be_nil
53
+ response.headers['Allow'].should eql('GET, POST, OPTIONS')
54
+ end
55
+ end
56
+
57
+ describe "#call" do
58
+ let :resource do
59
+ GetResource
60
+ end
61
+ it "should perform action" do
62
+ request.action = :get
63
+ obj = resource.new(request, response)
64
+ obj.should_receive(:get).and_return('res')
65
+ obj.call.should eql('res')
66
+ end
67
+
68
+ end
69
+
70
+ describe "#method_allowed?" do
71
+ let :resource do
72
+ GetPostResource
73
+ end
74
+
75
+ it "should be true on valid method" do
76
+ request.action = :get
77
+ obj = resource.new(request, response)
78
+ obj.method_allowed?.should be_true
79
+ end
80
+
81
+ it "should be false on invalid method" do
82
+ request.action = :put
83
+ obj = resource.new(request, response)
84
+ obj.method_allowed?.should be_false
85
+ end
86
+ end
87
+
88
+ describe "basic callback responses" do
89
+ let :resource do
90
+ GetPostResource
91
+ end
92
+
93
+ let :obj do
94
+ request.action = :get
95
+ obj = resource.new(request, response)
96
+ end
97
+
98
+ it "should all be true for questions" do
99
+ obj.exists?.should be_true
100
+ obj.authorized?.should be_true
101
+ obj.allowed?.should be_true
102
+ end
103
+
104
+ it "should be nil for values" do
105
+ obj.last_modified.should be_nil
106
+ obj.etag.should be_nil
107
+ end
108
+ end
109
+
110
+ describe "#check_callbacks" do
111
+ let :resource do
112
+ Class.new(GetPostResource) do
113
+ def head; nil; end
114
+ def put; nil; end
115
+ def delete; nil; end
116
+ end
117
+ end
118
+
119
+ let :obj do
120
+ request.action = :get
121
+ obj = resource.new(request, response)
122
+ end
123
+
124
+ it "should all be good by default" do
125
+ expect {
126
+ obj.check_callbacks
127
+ }.to_not raise_error
128
+ end
129
+
130
+ it "should raise error on invalid method" do
131
+ obj.stub(:method_allowed?).and_return(false)
132
+ expect {
133
+ obj.check_callbacks
134
+ }.to raise_error(Restfulness::HTTPException, "Method Not Allowed")
135
+ end
136
+
137
+ [:head, :get, :put, :delete].each do |action|
138
+ it "should raise error when not exists for #{action.to_s.upcase}" do
139
+ request.action = action
140
+ obj.stub(:exists?).and_return(false)
141
+ expect {
142
+ obj.check_callbacks
143
+ }.to raise_error(Restfulness::HTTPException, "Resource Not Found")
144
+ end
145
+ end
146
+
147
+ [:post].each do |action|
148
+ it "should not check exists? for #{action.to_s.upcase}" do
149
+ obj.request.action = action
150
+ obj.should_not_receive(:exists?)
151
+ obj.check_callbacks
152
+ end
153
+ end
154
+
155
+ it "should raise error when not authorized" do
156
+ obj.stub(:authorized?).and_return(false)
157
+ expect {
158
+ obj.check_callbacks
159
+ }.to raise_error(Restfulness::HTTPException, "Unauthorized")
160
+ end
161
+
162
+ it "should raise error when not allowed" do
163
+ obj.stub(:allowed?).and_return(false)
164
+ expect {
165
+ obj.check_callbacks
166
+ }.to raise_error(Restfulness::HTTPException, "Forbidden")
167
+ end
168
+
169
+ describe "with etag" do
170
+ it "should raise error when equal" do
171
+ obj.stub(:etag).and_return('sometag')
172
+ request.headers[:if_none_match] = 'sometag'
173
+ expect {
174
+ obj.check_callbacks
175
+ }.to raise_error(Restfulness::HTTPException, "Not Modified")
176
+ end
177
+
178
+ it "should continue if not equal" do
179
+ obj.stub(:etag).and_return('sometag')
180
+ request.headers[:if_none_match] = 'someoldtag'
181
+ expect {
182
+ obj.check_callbacks
183
+ }.to_not raise_error
184
+ end
185
+
186
+ it "should not be called unless action is :get or :head" do
187
+ obj.should_not_receive(:etag)
188
+ request.headers[:if_none_match] = 'sometag'
189
+ [:post, :put, :delete].each do |action|
190
+ request.action = action
191
+ obj.check_callbacks
192
+ end
193
+ end
194
+ end
195
+
196
+ describe "with if modified" do
197
+ it "should raise error when equal" do
198
+ time = Time.now
199
+ obj.stub(:last_modified).and_return(time)
200
+ request.headers[:if_modified_since] = time.to_s
201
+ expect {
202
+ obj.check_callbacks
203
+ }.to raise_error(Restfulness::HTTPException, "Not Modified")
204
+ end
205
+
206
+ it "should continue if not equal" do
207
+ time = Time.now
208
+ obj.stub(:last_modified).and_return(time)
209
+ request.headers[:if_modified_since] = (time - 60).to_s
210
+ expect {
211
+ obj.check_callbacks
212
+ }.to_not raise_error
213
+ end
214
+
215
+ it "should not be called unless action is :get or :head" do
216
+ obj.should_not_receive(:last_modified)
217
+ request.headers[:if_modified_since] = 'somedate'
218
+ [:post, :put, :delete].each do |action|
219
+ request.action = action
220
+ obj.check_callbacks
221
+ end
222
+ end
223
+
224
+ end
225
+ end
226
+
227
+ describe "#error" do
228
+
229
+ class Get418Resource < Restfulness::Resource
230
+ def get
231
+ error(418, {})
232
+ end
233
+ end
234
+
235
+ it "should raise a new exception" do
236
+ klass = Get418Resource
237
+ obj = klass.new(request, response)
238
+ expect {
239
+ obj.get
240
+ }.to raise_error(Restfulness::HTTPException, "I'm A Teapot")
241
+ end
242
+
243
+ end
244
+
245
+ end