restfulness 0.1.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.
@@ -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