paul-resourceful 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/MIT-LICENSE +21 -0
  2. data/Manifest.txt +34 -0
  3. data/README.markdown +86 -0
  4. data/Rakefile +14 -0
  5. data/lib/resourceful.rb +29 -0
  6. data/lib/resourceful/authentication_manager.rb +107 -0
  7. data/lib/resourceful/cache_manager.rb +174 -0
  8. data/lib/resourceful/header.rb +31 -0
  9. data/lib/resourceful/http_accessor.rb +85 -0
  10. data/lib/resourceful/net_http_adapter.rb +60 -0
  11. data/lib/resourceful/options_interpreter.rb +78 -0
  12. data/lib/resourceful/request.rb +63 -0
  13. data/lib/resourceful/resource.rb +266 -0
  14. data/lib/resourceful/response.rb +175 -0
  15. data/lib/resourceful/stubbed_resource_proxy.rb +47 -0
  16. data/lib/resourceful/util.rb +6 -0
  17. data/lib/resourceful/version.rb +1 -0
  18. data/resourceful.gemspec +30 -0
  19. data/spec/acceptance_shared_specs.rb +49 -0
  20. data/spec/acceptance_spec.rb +408 -0
  21. data/spec/resourceful/authentication_manager_spec.rb +249 -0
  22. data/spec/resourceful/cache_manager_spec.rb +211 -0
  23. data/spec/resourceful/header_spec.rb +38 -0
  24. data/spec/resourceful/http_accessor_spec.rb +125 -0
  25. data/spec/resourceful/net_http_adapter_spec.rb +96 -0
  26. data/spec/resourceful/options_interpreter_spec.rb +94 -0
  27. data/spec/resourceful/request_spec.rb +186 -0
  28. data/spec/resourceful/resource_spec.rb +600 -0
  29. data/spec/resourceful/response_spec.rb +238 -0
  30. data/spec/resourceful/stubbed_resource_proxy_spec.rb +58 -0
  31. data/spec/simple_http_server_shared_spec.rb +160 -0
  32. data/spec/simple_http_server_shared_spec_spec.rb +212 -0
  33. data/spec/spec.opts +3 -0
  34. data/spec/spec_helper.rb +14 -0
  35. metadata +98 -0
@@ -0,0 +1,249 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname + '../spec_helper'
3
+
4
+ require 'resourceful/authentication_manager'
5
+
6
+ describe Resourceful::AuthenticationManager do
7
+
8
+ before do
9
+ @authmgr = Resourceful::AuthenticationManager.new
10
+
11
+ @authenticator = mock('Authenticator')
12
+ end
13
+
14
+ [:add_auth_handler, :associate_auth_info, :add_credentials].each do |meth|
15
+ it "should have ##{meth}" do
16
+ @authmgr.should respond_to(meth)
17
+ end
18
+ end
19
+
20
+ it 'should add an authenticator to its list' do
21
+ @authmgr.add_auth_handler(@authenticator)
22
+ @authmgr.instance_variable_get("@authenticators").should include(@authenticator)
23
+ end
24
+
25
+ describe 'associating authenticators with challanges' do
26
+ before do
27
+ @authmgr.add_auth_handler(@authenticator)
28
+ @authenticator.stub!(:valid_for?).and_return(true)
29
+ @authenticator.stub!(:update_credentials)
30
+ @challenge = mock('Response')
31
+ end
32
+
33
+ it 'should check that an authenticator is valid for a challenge' do
34
+ @authenticator.should_receive(:valid_for?).with(@challenge).and_return(true)
35
+ @authmgr.associate_auth_info(@challenge)
36
+ end
37
+
38
+ it 'should update the credentials of the authenticator if it is valid for the challenge' do
39
+ @authenticator.should_receive(:update_credentials).with(@challenge)
40
+ @authmgr.associate_auth_info(@challenge)
41
+ end
42
+
43
+ it 'should not update the credentials of the authenticator if it is not valid for the challenge' do
44
+ @authenticator.stub!(:valid_for?).and_return(false)
45
+ @authenticator.should_not_receive(:update_credentials).with(@challenge)
46
+ @authmgr.associate_auth_info(@challenge)
47
+ end
48
+
49
+ end
50
+
51
+ describe 'adding credentials to a request' do
52
+ before do
53
+ @authmgr.add_auth_handler(@authenticator)
54
+ @authenticator.stub!(:can_handle?).and_return(true)
55
+ @authenticator.stub!(:add_credentials_to)
56
+ @request = mock('Request')
57
+ end
58
+
59
+ it 'should find an authenticator that can handle the request' do
60
+ @authenticator.should_receive(:can_handle?).with(@request).and_return(true)
61
+ @authmgr.add_credentials(@request)
62
+ end
63
+
64
+ it 'should add the authenticators credentials to the request' do
65
+ @authenticator.should_receive(:add_credentials_to).with(@request)
66
+ @authmgr.add_credentials(@request)
67
+ end
68
+
69
+ it 'should not add the authenticators credentials to the request if it cant handle it' do
70
+ @authenticator.should_receive(:can_handle?).with(@request).and_return(false)
71
+ @authenticator.should_not_receive(:add_credentials_to).with(@request)
72
+ @authmgr.add_credentials(@request)
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ describe Resourceful::BasicAuthenticator do
80
+ before do
81
+ @auth = Resourceful::BasicAuthenticator.new('Test Auth', 'admin', 'secret')
82
+ end
83
+
84
+ {:realm => 'Test Auth', :username => 'admin', :password => 'secret'}.each do |meth,val|
85
+ it "should initialize with a #{meth}" do
86
+ @auth.instance_variable_get("@#{meth}").should == val
87
+ end
88
+ end
89
+
90
+ describe "Updating from a challenge response" do
91
+ before do
92
+ @header = {'WWW-Authenticate' => ['Basic realm="Test Auth"']}
93
+ @chal = mock('response', :header => @header, :uri => 'http://example.com/foo/bar')
94
+ end
95
+
96
+ it 'should be valid for a challenge response with scheme "Basic" and the same realm' do
97
+ @auth.valid_for?(@chal).should be_true
98
+ end
99
+
100
+ it 'should be valid for a challenge response with multiple schemes including matchin "Basic" challenge' do
101
+ @header = {'WWW-Authenticate' => ['Digest some other stuff', 'Basic realm="Test Auth"', 'Weird scheme']}
102
+
103
+ @auth.valid_for?(@chal).should be_true
104
+ end
105
+
106
+ it 'should not be sensitive to case variances in the scheme' do
107
+ @header['WWW-Authenticate'] = ['bAsIc realm="Test Auth"']
108
+ @auth.valid_for?(@chal).should be_true
109
+ end
110
+
111
+ it 'should not be sensitive to case variances in the realm directive' do
112
+ @header['WWW-Authenticate'] = ['Basic rEaLm="Test Auth"']
113
+ @auth.valid_for?(@chal).should be_true
114
+ end
115
+
116
+ it 'should not be sensitive to case variances in the realm value' do
117
+ @header['WWW-Authenticate'] = ['Basic realm="test auth"']
118
+ @auth.valid_for?(@chal).should be_true
119
+ end
120
+
121
+ it 'should not be valid if the scheme is not "Basic"' do
122
+ @header['WWW-Authenticate'] = ["Digest"]
123
+ @auth.valid_for?(@chal).should be_false
124
+ end
125
+
126
+ it 'should not be valid if the realm does not match' do
127
+ @header['WWW-Authenticate'] = ['Basic realm="not test auth"']
128
+ @auth.valid_for?(@chal).should be_false
129
+ end
130
+
131
+ it 'should not be valid if the header is unreadable' do
132
+ @header['WWW-Authenticate'] = nil
133
+ @auth.valid_for?(@chal).should be_false
134
+ end
135
+
136
+ it 'should set the valid domain from the host part of the challenge uri' do
137
+ @auth.update_credentials(@chal)
138
+ @auth.instance_variable_get("@domain").should == 'example.com'
139
+ end
140
+
141
+ end
142
+
143
+ describe 'updating a request with credentials' do
144
+ before do
145
+ @auth.instance_variable_set("@domain", 'example.com')
146
+ @header = {}
147
+ @req = mock('request', :uri => 'http://example.com/bar/foo', :header => @header)
148
+ end
149
+
150
+ it 'should be able to handle a request for the matching domain' do
151
+ @auth.can_handle?(@req).should be_true
152
+ end
153
+
154
+ it 'should add credentials to a request' do
155
+ @header.should_receive(:[]=).with('Authorization', 'Basic YWRtaW46c2VjcmV0')
156
+ @auth.add_credentials_to(@req)
157
+ end
158
+
159
+ it 'should build the credentials string for the header' do
160
+ @auth.credentials.should == 'Basic YWRtaW46c2VjcmV0'
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ describe Resourceful::DigestAuthenticator do
167
+
168
+ before do
169
+ @header = {'WWW-Authenticate' => ['Digest realm="Test Auth"']}
170
+ @chal = mock('response', :header => @header, :uri => 'http://example.com/foo/bar')
171
+
172
+ @req_header = {}
173
+ @req = mock('request', :header => @req_header,
174
+ :uri => 'http://example.com',
175
+ :method => 'GET')
176
+
177
+ @auth = Resourceful::DigestAuthenticator.new('Test Auth', 'admin', 'secret')
178
+ end
179
+
180
+ {:realm => 'Test Auth', :username => 'admin', :password => 'secret'}.each do |meth,val|
181
+ it "should initialize with a #{meth}" do
182
+ @auth.instance_variable_get("@#{meth}").should == val
183
+ end
184
+ end
185
+
186
+ describe "Updating credentials from a challenge response" do
187
+
188
+ it "should set the domain from the host part of the challenge response uri" do
189
+ @auth.update_credentials(@chal)
190
+ @auth.domain.should == 'example.com'
191
+ end
192
+
193
+ it "should create an HTTPAuth Digest Challenge from the challenge response WWW-Authenticate header" do
194
+ HTTPAuth::Digest::Challenge.should_receive(:from_header).with(@header['WWW-Authenticate'].first)
195
+ @auth.update_credentials(@chal)
196
+ end
197
+
198
+ end
199
+
200
+ describe "Validating a challenge" do
201
+ it 'should be valid for a challenge response with scheme "Digest" and the same realm' do
202
+ @auth.valid_for?(@chal).should be_true
203
+ end
204
+
205
+ it 'should not be valid if the scheme is not "Digest"' do
206
+ @header['WWW-Authenticate'] = ["Basic"]
207
+ @auth.valid_for?(@chal).should be_false
208
+ end
209
+
210
+ it 'should not be valid if the realm does not match' do
211
+ @header['WWW-Authenticate'] = ['Digest realm="not test auth"']
212
+ @auth.valid_for?(@chal).should be_false
213
+ end
214
+
215
+ it 'should not be valid if the header is unreadable' do
216
+ @header['WWW-Authenticate'] = nil
217
+ @auth.valid_for?(@chal).should be_false
218
+ end
219
+ end
220
+
221
+ it "should be able to handle requests to the same domain" do
222
+ @auth.instance_variable_set("@domain", 'example.com')
223
+ @auth.can_handle?(@req).should be_true
224
+ end
225
+
226
+ it "should not handle requests to a different domain" do
227
+ @auth.instance_variable_set("@domain", 'example2.com')
228
+ @auth.can_handle?(@req).should be_false
229
+ end
230
+
231
+ it "should add credentials to a request" do
232
+ @auth.update_credentials(@chal)
233
+ @auth.add_credentials_to(@req)
234
+ @req_header.should have_key('Authorization')
235
+ @req_header['Authorization'].should_not be_blank
236
+ end
237
+
238
+ it "should have HTTPAuth::Digest generate the Authorization header" do
239
+ @auth.update_credentials(@chal)
240
+ cred = mock('digest_credentials', :to_header => nil)
241
+
242
+ HTTPAuth::Digest::Credentials.should_receive(:from_challenge).with(
243
+ @auth.challenge, :username => 'admin', :password => 'secret', :method => 'GET', :uri => ''
244
+ ).and_return(cred)
245
+
246
+ cred = @auth.credentials_for(@req)
247
+ end
248
+
249
+ end
@@ -0,0 +1,211 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname + '../spec_helper'
3
+
4
+ require 'resourceful/cache_manager'
5
+
6
+ describe Resourceful::CacheManager do
7
+ before do
8
+ @cm = Resourceful::InMemoryCacheManager.new #cheat, because I cant new a real one.
9
+ end
10
+
11
+ it 'should not be initializable' do
12
+ lambda { Resourceful::CacheManager.new }.should raise_error
13
+ end
14
+
15
+ it 'should have a lookup method' do
16
+ @cm.should respond_to(:lookup)
17
+ end
18
+
19
+ it 'should have a store method' do
20
+ @cm.should respond_to(:store)
21
+ end
22
+
23
+ it 'should have a invalidate method' do
24
+ @cm.should respond_to(:invalidate)
25
+ end
26
+
27
+ describe '#select_request_headers' do
28
+ before do
29
+ @req_header = mock('header', :[] => nil)
30
+ @request = mock('request', :header => @req_header)
31
+
32
+ @resp_header = mock('header', :[] => nil)
33
+ @response = mock('response', :header => @resp_header)
34
+ end
35
+
36
+ it 'should select the request headers from the Vary header' do
37
+ @resp_header.should_receive(:[]).with('Vary')
38
+ @cm.select_request_headers(@request, @response)
39
+ end
40
+
41
+ it 'should pull the values from the request that match keys in the vary header' do
42
+ @resp_header.should_receive(:[]).with('Vary').twice.and_return(['foo, bar'])
43
+ @req_header.should_receive(:[]).with('foo').and_return('oof')
44
+ @req_header.should_receive(:[]).with('bar').and_return('rab')
45
+
46
+ header = @cm.select_request_headers(@request, @response)
47
+ header['foo'].should == 'oof'
48
+ header['bar'].should == 'rab'
49
+ end
50
+
51
+ it 'should return a new Header object' do
52
+ @cm.select_request_headers(@request, @response).should be_kind_of(Resourceful::Header)
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ describe Resourceful::NullCacheManager do
59
+ before do
60
+ @ncm = Resourceful::NullCacheManager.new
61
+ end
62
+
63
+ it 'should not find anything' do
64
+ @ncm.lookup(:stuff).should be_nil
65
+ end
66
+
67
+ it 'should not store anything' do
68
+ @ncm.should respond_to(:store)
69
+
70
+ lambda { @ncm.store(:foo, :bar) }.should_not raise_error
71
+ end
72
+
73
+ end
74
+
75
+ describe Resourceful::InMemoryCacheManager do
76
+ before do
77
+ @request = mock('request', :resource => mock('resource'),
78
+ :request_time => Time.utc(2008,5,22,15,00),
79
+ :uri => 'uri')
80
+ @response = mock('response', :header => {}, :cachable? => true)
81
+
82
+ @entry = mock('cache entry', :response => @response, :valid_for? => true)
83
+ Resourceful::InMemoryCacheManager::CacheEntry.stub!(:new).and_return(@entry)
84
+
85
+ @imcm = Resourceful::InMemoryCacheManager.new
86
+ end
87
+
88
+ describe 'finding' do
89
+ before do
90
+ @response.stub!(:authoritative=)
91
+ @imcm.instance_variable_set("@collection", {'uri' => {@request => @entry}})
92
+ end
93
+
94
+ it 'should lookup the response by request' do
95
+ @imcm.lookup(@request).should == @response
96
+ end
97
+
98
+ it 'should set the response to non-authoritative' do
99
+ @response.should_receive(:authoritative=).with(false)
100
+ @imcm.lookup(@request)
101
+ end
102
+ end
103
+
104
+ describe 'saving' do
105
+ it 'should make a new cache entry' do
106
+ Resourceful::InMemoryCacheManager::CacheEntry.should_receive(:new).with(
107
+ Time.utc(2008,5,22,15,00),
108
+ {},
109
+ @response
110
+ )
111
+
112
+ @imcm.store(@request, @response)
113
+ end
114
+
115
+ it 'should store the response entity by request' do
116
+ @imcm.store(@request, @response)
117
+ col = @imcm.instance_variable_get("@collection")
118
+ col['uri'][@request].response.should == @response
119
+ end
120
+
121
+ it 'should check if the response is cachable' do
122
+ @response.should_receive(:cachable?).and_return(true)
123
+ @imcm.store(@request, @response)
124
+ end
125
+
126
+ it 'should not store an entry if the response is not cachable' do
127
+ @response.should_receive(:cachable?).and_return(false)
128
+ @imcm.store(@request, @response)
129
+ col = @imcm.instance_variable_get("@collection")
130
+ col['uri'][@request].should be_nil
131
+ end
132
+ end
133
+
134
+ describe 'invalidating' do
135
+ it 'should remove an entry from the cache by uri' do
136
+ @imcm.store(@request, @response)
137
+ @imcm.invalidate('uri')
138
+ col = @imcm.instance_variable_get("@collection")
139
+ col.should_not have_key('uri')
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ describe Resourceful::InMemoryCacheManager::CacheEntryCollection do
146
+ before do
147
+ @entry_valid = mock('entry', :valid_for? => true)
148
+ @entry_invalid = mock('entry', :valid_for? => false)
149
+
150
+ @request = mock('request')
151
+
152
+ @collection = Resourceful::InMemoryCacheManager::CacheEntryCollection.new
153
+ end
154
+
155
+ it 'should find the right entry for a request' do
156
+ @collection.instance_variable_set('@entries', [@entry_valid, @entry_invalid])
157
+ @entry_valid.should_receive(:valid_for?).with(@request).and_return(true)
158
+ @collection[@request].should == @entry_valid
159
+ end
160
+
161
+ it 'should be nil if no matching entry was found' do
162
+ @collection.instance_variable_set('@entries', [@entry_invalid])
163
+ @entry_invalid.should_receive(:valid_for?).with(@request).and_return(false)
164
+ @collection[@request].should == nil
165
+ end
166
+
167
+ it 'should store an entry' do
168
+ @collection[@request] = @entry_valid
169
+ @collection.instance_variable_get("@entries").should include(@entry_valid)
170
+ end
171
+
172
+ it 'should replace an existing entry if the existing entry matches the request' do
173
+ @new_entry = mock('entry', :valid_for? => true)
174
+
175
+ @collection[@request] = @entry_valid
176
+ @collection[@request] = @new_entry
177
+
178
+ @collection.instance_variable_get("@entries").should include(@new_entry)
179
+ @collection.instance_variable_get("@entries").should_not include(@entry_valid)
180
+ end
181
+
182
+ end
183
+
184
+ describe Resourceful::InMemoryCacheManager::CacheEntry do
185
+ before do
186
+ @entry = Resourceful::InMemoryCacheManager::CacheEntry.new(
187
+ Time.utc(2008,5,16,0,0,0), {'Content-Type' => 'text/plain'}, mock('response')
188
+ )
189
+
190
+ @request = mock('request')
191
+ end
192
+
193
+ [:request_time, :request_vary_headers, :response, :valid_for?].each do |method|
194
+ it "should respond to ##{method}" do
195
+ @entry.should respond_to(method)
196
+ end
197
+ end
198
+
199
+ it 'should be valid for a request if all the vary headers match' do
200
+ @request.stub!(:header).and_return({'Content-Type' => 'text/plain'})
201
+ @entry.valid_for?(@request).should be_true
202
+ end
203
+
204
+ it 'should not be valid for a request if not all the vary headers match' do
205
+ @request.stub!(:header).and_return({'Content-Type' => 'text/html'})
206
+ @entry.valid_for?(@request).should be_false
207
+ end
208
+
209
+ end
210
+
211
+