nkallen-cash 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,190 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ module Cash
4
+ describe Finders do
5
+ describe 'when the cache is populated' do
6
+ describe "#find" do
7
+ describe '#find(id...)' do
8
+ describe '#find(id)' do
9
+ it "returns an active record" do
10
+ story = Story.create!(:title => 'a story')
11
+ Story.find(story.id).should == story
12
+ end
13
+ end
14
+
15
+ describe 'when the object is destroyed' do
16
+ describe '#find(id)' do
17
+ it "raises an error" do
18
+ story = Story.create!(:title => "I am delicious")
19
+ story.destroy
20
+ lambda { Story.find(story.id) }.should raise_error(ActiveRecord::RecordNotFound)
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#find(id1, id2, ...)' do
26
+ it "returns an array" do
27
+ story1, story2 = Story.create!, Story.create!
28
+ Story.find(story1.id, story2.id).should == [story1, story2]
29
+ end
30
+
31
+ describe "#find(id, nil)" do
32
+ it "ignores the nils" do
33
+ story = Story.create!
34
+ Story.find(story.id, nil).should == story
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'when given nonexistent ids' do
40
+ describe 'when given one nonexistent id' do
41
+ it 'raises an error' do
42
+ lambda { Story.find(1) }.should raise_error(ActiveRecord::RecordNotFound)
43
+ end
44
+ end
45
+
46
+ describe 'when given multiple nonexistent ids' do
47
+ it "raises an error" do
48
+ lambda { Story.find(1, 2, 3) }.should raise_error(ActiveRecord::RecordNotFound)
49
+ end
50
+ end
51
+
52
+
53
+ describe '#find(nil)' do
54
+ it 'raises an error' do
55
+ lambda { Story.find(nil) }.should raise_error(ActiveRecord::RecordNotFound)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#find(object)' do
62
+ it "coerces arguments to integers" do
63
+ story = Story.create!
64
+ Story.find(story.id.to_s).should == story
65
+ end
66
+ end
67
+
68
+ describe '#find([...])' do
69
+ describe 'when given an array with valid ids' do
70
+ it "#finds the object with that id" do
71
+ story = Story.create!
72
+ Story.find([story.id]).should == [story]
73
+ end
74
+ end
75
+
76
+ describe '#find([])' do
77
+ it 'returns the empty array' do
78
+ Story.find([]).should == []
79
+ end
80
+ end
81
+
82
+ describe 'when given nonexistent ids' do
83
+ it 'raises an error' do
84
+ lambda { Story.find([1, 2, 3]) }.should raise_error(ActiveRecord::RecordNotFound)
85
+ end
86
+ end
87
+
88
+ describe 'when given limits and offsets' do
89
+ describe '#find([1, 2, ...], :limit => ..., :offset => ...)' do
90
+ it "returns the correct slice of objects" do
91
+ character1 = Character.create!(:name => "Sam", :story_id => 1)
92
+ character2 = Character.create!(:name => "Sam", :story_id => 1)
93
+ character3 = Character.create!(:name => "Sam", :story_id => 1)
94
+ Character.find(
95
+ [character1.id, character2.id, character3.id],
96
+ :conditions => { :name => "Sam", :story_id => 1 }, :limit => 2
97
+ ).should == [character1, character2]
98
+ end
99
+ end
100
+
101
+ describe '#find([1], :limit => 0)' do
102
+ it "raises an error" do
103
+ character = Character.create!(:name => "Sam", :story_id => 1)
104
+ lambda do
105
+ Character.find([character.id], :conditions => { :name => "Sam", :story_id => 1 }, :limit => 0)
106
+ end.should raise_error(ActiveRecord::RecordNotFound)
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#find(:first, ..., :offset => ...)' do
113
+ it "#finds the object in the correct order" do
114
+ story1 = Story.create!(:title => 'title1')
115
+ story2 = Story.create!(:title => story1.title)
116
+ Story.find(:first, :conditions => { :title => story1.title }, :offset => 1).should == story2
117
+ end
118
+ end
119
+
120
+ describe '#find(:first, :conditions => [])' do
121
+ it 'works' do
122
+ story = Story.create!
123
+ Story.find(:first, :conditions => []).should == story
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#find_by_attr' do
129
+ describe '#find_by_attr(nil)' do
130
+ it 'returns nil' do
131
+ Story.find_by_id(nil).should == nil
132
+ end
133
+ end
134
+
135
+ describe 'when given non-existent ids' do
136
+ it 'returns nil' do
137
+ Story.find_by_id(-1).should == nil
138
+ end
139
+ end
140
+ end
141
+
142
+ describe '#find_all_by_attr' do
143
+ describe 'when given non-existent ids' do
144
+ it "does not raise an error" do
145
+ lambda { Story.find_all_by_id([-1, -2, -3]) }.should_not raise_error
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ describe 'when the cache is partially populated' do
152
+ describe '#find(:all, :conditions => ...)' do
153
+ it "returns the correct records" do
154
+ story1 = Story.create!(:title => title = 'once upon a time...')
155
+ $memcache.flush_all
156
+ story2 = Story.create!(:title => title)
157
+ Story.find(:all, :conditions => { :title => story1.title }).should == [story1, story2]
158
+ end
159
+ end
160
+
161
+ describe '#find(id1, id2, ...)' do
162
+ it "returns the correct records" do
163
+ story1 = Story.create!(:title => 'story 1')
164
+ $memcache.flush_all
165
+ story2 = Story.create!(:title => 'story 2')
166
+ Story.find(story1.id, story2.id).should == [story1, story2]
167
+ end
168
+ end
169
+ end
170
+
171
+ describe 'when the cache is not populated' do
172
+ describe '#find(id)' do
173
+ it "returns the correct records" do
174
+ story = Story.create!(:title => 'a story')
175
+ $memcache.flush_all
176
+ Story.find(story.id).should == story
177
+ end
178
+ end
179
+
180
+ describe '#find(id1, id2, ...)' do
181
+ it "handles finds with multiple ids correctly" do
182
+ story1 = Story.create!
183
+ story2 = Story.create!
184
+ $memcache.flush_all
185
+ Story.find(story1.id, story2.id).should == [story1, story2]
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ module Cash
4
+ describe Finders do
5
+ describe 'Calculations' do
6
+ describe 'when the cache is populated' do
7
+ before do
8
+ @stories = [Story.create!(:title => @title = 'asdf'), Story.create!(:title => @title)]
9
+ end
10
+
11
+ describe '#count(:all, :conditions => ...)' do
12
+ it "does not use the database" do
13
+ mock(Story.connection).execute.never
14
+ Story.count(:all, :conditions => { :title => @title }).should == @stories.size
15
+ end
16
+ end
17
+
18
+ describe '#count(:column, :conditions => ...)' do
19
+ it "uses the database, not the cache" do
20
+ mock(Story).get.never
21
+ Story.count(:title, :conditions => { :title => @title }).should == @stories.size
22
+ end
23
+ end
24
+
25
+ describe '#count(:all, :distinct => ..., :select => ...)' do
26
+ it 'uses the database, not the cache' do
27
+ mock(Story).get.never
28
+ Story.count(:all, :distinct => true, :select => :title, :conditions => { :title => @title }).should == @stories.collect(&:title).uniq.size
29
+ end
30
+ end
31
+
32
+ describe 'association proxies' do
33
+ describe '#count(:all, :conditions => ...)' do
34
+ it 'does not use the database' do
35
+ story = Story.create!
36
+ characters = [story.characters.create!(:name => name = 'name'), story.characters.create!(:name => name)]
37
+ mock(Story.connection).execute.never
38
+ story.characters.count(:all, :conditions => { :name => name }).should == characters.size
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'when the cache is not populated' do
45
+ describe '#count(:all, ...)' do
46
+ describe '#count(:all)' do
47
+ it 'uses the database, not the cache' do
48
+ mock(Story).get.never
49
+ Story.count
50
+ end
51
+ end
52
+
53
+ describe '#count(:all, :conditions => ...)' do
54
+ it "populates the count correctly" do
55
+ Story.create!(:title => title = 'title')
56
+ $memcache.flush_all
57
+ Story.count(:all, :conditions => { :title => title }).should == 1
58
+ Story.fetch("title/#{title}/count").should =~ /1/
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,336 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ module Cash
4
+ describe Finders do
5
+ describe 'Cache Usage' do
6
+ describe 'when the cache is populated' do
7
+ describe '#find' do
8
+ describe '#find(1)' do
9
+ it 'does not use the database' do
10
+ story = Story.create!
11
+ mock(Story.connection).execute.never
12
+ Story.find(story.id).should == story
13
+ end
14
+ end
15
+
16
+ describe '#find(object)' do
17
+ it 'uses the objects quoted id' do
18
+ story = Story.create!
19
+ mock(Story.connection).execute.never
20
+ Story.find(story).should == story
21
+ end
22
+ end
23
+
24
+ describe '#find(:first, ...)' do
25
+ describe '#find(:first, :conditions => { :id => ?})' do
26
+ it "does not use the database" do
27
+ story = Story.create!
28
+ mock(Story.connection).execute.never
29
+ Story.find(:first, :conditions => { :id => story.id }).should == story
30
+ end
31
+ end
32
+
33
+ describe "#find(:first, :conditions => 'id = ?')" do
34
+ it "does not use the database" do
35
+ story = Story.create!
36
+ mock(Story.connection).execute.never
37
+ Story.find(:first, :conditions => "id = #{story.id}").should == story
38
+ Story.find(:first, :conditions => "`stories`.id = #{story.id}").should == story
39
+ Story.find(:first, :conditions => "`stories`.`id` = #{story.id}").should == story
40
+ end
41
+ end
42
+
43
+ describe '#find(:first, :readonly => false) and any other options other than conditions are nil' do
44
+ it "does not use the database" do
45
+ story = Story.create!
46
+ mock(Story.connection).execute.never
47
+ Story.find(:first, :conditions => { :id => story.id }, :readonly => false, :limit => nil, :offset => nil, :joins => nil, :include => nil).should == story
48
+ end
49
+ end
50
+
51
+ describe '#find(:first, :readonly => true)' do
52
+ it "uses the database, not the cache" do
53
+ story = Story.create!
54
+ mock(Story).get.never
55
+ Story.find(:first, :conditions => { :id => story.id }, :readonly => true).should == story
56
+ end
57
+ end
58
+
59
+ describe '#find(:first, :join => ...) or find(..., :include => ...)' do
60
+ it "uses the database, not the cache" do
61
+ story = Story.create!
62
+ mock(Story).get.never
63
+ Story.find(:first, :conditions => { :id => story.id }, :joins => 'AS stories').should == story
64
+ Story.find(:first, :conditions => { :id => story.id }, :include => :characters).should == story
65
+ end
66
+ end
67
+
68
+ describe '#find(:first)' do
69
+ it 'uses the database, not the cache' do
70
+ mock(Story).get.never
71
+ Story.find(:first)
72
+ end
73
+ end
74
+
75
+ describe '#find(:first, :conditions => "...")' do
76
+ describe 'on unindexed attributes' do
77
+ it 'uses the database, not the cache' do
78
+ story = Story.create!
79
+ mock(Story).get.never
80
+ Story.find(:first, :conditions => "type IS NULL")
81
+ end
82
+ end
83
+
84
+ describe 'on indexed attributes' do
85
+ describe 'when the attributes are integers' do
86
+ it 'does not use the database' do
87
+ story = Story.create!
88
+ mock(Story.connection).execute.never
89
+ Story.find(:first, :conditions => "`stories`.id = #{story.id}") \
90
+ .should == story
91
+ end
92
+ end
93
+
94
+ describe 'when the attributes are non-integers' do
95
+ it 'uses the database, not the cache' do
96
+ story = Story.create!(:title => "title")
97
+ mock(Story.connection).execute.never
98
+ Story.find(:first, :conditions => "`stories`.title = '#{story.title }'") \
99
+ .should == story
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '#find(:first, :conditions => [...])' do
105
+ describe 'with one indexed attribute' do
106
+ it 'does not use the database' do
107
+ story = Story.create!
108
+ mock(Story.connection).execute.never
109
+ Story.find(:first, :conditions => ['id = ?', story.id]).should == story
110
+ end
111
+ end
112
+
113
+ describe 'with two attributes that match a combo-index' do
114
+ it 'does not use the database' do
115
+ story = Story.create!(:title => 'title')
116
+ mock(Story.connection).execute.never
117
+ Story.find(:first, :conditions => ['id = ? AND title = ?', story.id, story.title]).should == story
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '#find(:first, :conditions => {...})' do
124
+ it "does not use the database" do
125
+ story = Story.create!(:title => "Sam")
126
+ mock(Story.connection).execute.never
127
+ Story.find(:first, :conditions => { :id => story.id, :title => story.title }).should == story
128
+ end
129
+
130
+ describe 'regardless of hash order' do
131
+ it 'does not use the database' do
132
+ story = Story.create!(:title => "Sam")
133
+ mock(Story.connection).execute.never
134
+ Story.find(:first, :conditions => { :id => story.id, :title => story.title }).should == story
135
+ Story.find(:first, :conditions => { :title => story.title, :id => story.id }).should == story
136
+ end
137
+ end
138
+
139
+ describe 'on unindexed attribtes' do
140
+ it 'uses the database, not the cache' do
141
+ story = Story.create!
142
+ mock(Story).get.never
143
+ Story.find(:first, :conditions => { :id => story.id, :type => story.type }).should == story
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ describe 'when there is a with_scope' do
150
+ describe 'when the with_scope has conditions' do
151
+ describe 'when the scope conditions is a string' do
152
+ it "uses the database, not the cache" do
153
+ story = Story.create!(:title => title = 'title')
154
+ mock(Story.connection).execute.never
155
+ Story.send :with_scope, :find => { :conditions => "title = '#{title}'"} do
156
+ Story.find(:first, :conditions => { :id => story.id }).should == story
157
+ end
158
+ end
159
+ end
160
+
161
+ describe 'when the find conditions is a string' do
162
+ it "does not use the database" do
163
+ story = Story.create!(:title => title = 'title')
164
+ mock(Story.connection).execute.never
165
+ Story.send :with_scope, :find => { :conditions => { :id => story.id }} do
166
+ Story.find(:first, :conditions => "title = '#{title}'").should == story
167
+ end
168
+ end
169
+ end
170
+
171
+ describe '#find(1, :conditions => ...)' do
172
+ it "does not use the database" do
173
+ story = Story.create!
174
+ character = Character.create!(:name => name = 'barbara', :story_id => story)
175
+ mock(Character.connection).execute.never
176
+ Character.send :with_scope, :find => { :conditions => { :story_id => story.id } } do
177
+ Character.find(character.id, :conditions => { :name => name }).should == character
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ describe 'has_many associations' do
184
+ describe '#find(1)' do
185
+ it "does not use the database" do
186
+ story = Story.create!
187
+ character = story.characters.create!
188
+ mock(Character.connection).execute.never
189
+ story.characters.find(character.id).should == character
190
+ end
191
+ end
192
+
193
+ describe '#find(1, 2, ...)' do
194
+ it "does not use the database" do
195
+ story = Story.create!
196
+ character1 = story.characters.create!
197
+ character2 = story.characters.create!
198
+ mock(Character.connection).execute.never
199
+ story.characters.find(character1.id, character2.id).should == [character1, character2]
200
+ end
201
+ end
202
+
203
+ describe '#find_by_attr' do
204
+ it "does not use the database" do
205
+ story = Story.create!
206
+ character = story.characters.create!
207
+ mock(Character.connection).execute.never
208
+ story.characters.find_by_id(character.id).should == character
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ describe '#find(:all)' do
215
+ it "uses the database, not the cache" do
216
+ character = Character.create!
217
+ mock(Character).get.never
218
+ Character.find(:all).should == [character]
219
+ end
220
+
221
+ describe '#find(:all, :conditions => {...})' do
222
+ describe 'when the index is not empty' do
223
+ it 'does not use the database' do
224
+ story1 = Story.create!(:title => title = "title")
225
+ story2 = Story.create!(:title => title)
226
+ mock(Story.connection).execute.never
227
+ Story.find(:all, :conditions => { :title => story1.title }).should == [story1, story2]
228
+ end
229
+ end
230
+ end
231
+
232
+ describe '#find(:all, :limit => ..., :offset => ...)' do
233
+ it "cached attributes should support limits and offsets" do
234
+ character1 = Character.create!(:name => "Sam", :story_id => 1)
235
+ character2 = Character.create!(:name => "Sam", :story_id => 1)
236
+ character3 = Character.create!(:name => "Sam", :story_id => 1)
237
+ mock(Character.connection).execute.never
238
+
239
+ Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :limit => 1).should == [character1]
240
+ Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :offset => 1).should == [character2, character3]
241
+ Character.find(:all, :conditions => { :name => character1.name, :story_id => character1.story_id }, :limit => 1, :offset => 1).should == [character2]
242
+ end
243
+ end
244
+ end
245
+
246
+ describe '#find([...])' do
247
+ describe '#find([1, 2, ...], :conditions => ...)' do
248
+ it "uses the database, not the cache" do
249
+ story1, story2 = Story.create!, Story.create!
250
+ mock(Story).get.never
251
+ Story.find([story1.id, story2.id], :conditions => "type IS NULL").should == [story1, story2]
252
+ end
253
+ end
254
+
255
+ describe '#find([1], :conditions => ...)' do
256
+ it "uses the database, not the cache" do
257
+ story1, story2 = Story.create!, Story.create!
258
+ mock(Story).get.never
259
+ Story.find([story1.id], :conditions => "type IS NULL").should == [story1]
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#find_by_attr' do
265
+ describe 'on indexed attributes' do
266
+ describe '#find_by_id(id)' do
267
+ it "does not use the database" do
268
+ story = Story.create!
269
+ mock(Story.connection).execute.never
270
+ Story.find_by_id(story.id).should == story
271
+ end
272
+ end
273
+
274
+ describe '#find_by_title(title)' do
275
+ it "does not use the database" do
276
+ story1 = Story.create!(:title => 'title1')
277
+ story2 = Story.create!(:title => 'title2')
278
+ mock(Story.connection).execute.never
279
+ Story.find_by_title('title1').should == story1
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ describe "Single Table Inheritence" do
286
+ describe '#find(:all, ...)' do
287
+ it "does not use the database" do
288
+ story, epic, oral = Story.create!(:title => title = 'foo'), Epic.create!(:title => title), Oral.create!(:title => title)
289
+ mock(Story.connection).execute.never
290
+ Story.find(:all, :conditions => { :title => title }).should == [story, epic, oral]
291
+ Epic.find(:all, :conditions => { :title => title }).should == [epic, oral]
292
+ Oral.find(:all, :conditions => { :title => title }).should == [oral]
293
+ end
294
+ end
295
+ end
296
+ end
297
+ end
298
+
299
+ describe 'when the cache is not populated' do
300
+ before do
301
+ @story = Story.create!(:title => 'title')
302
+ $memcache.flush_all
303
+ end
304
+
305
+ describe '#find(:first, ...)' do
306
+ it 'populates the cache' do
307
+ Story.find(:first, :conditions => { :title => @story.title })
308
+ Story.fetch("title/#{@story.title}").should == [@story.id]
309
+ end
310
+ end
311
+
312
+ describe '#find_by_attr' do
313
+ it 'populates the cache' do
314
+ Story.find_by_title(@story.title)
315
+ Story.fetch("title/#{@story.title}").should == [@story.id]
316
+ end
317
+ end
318
+
319
+ describe '#find(:all, :conditions => ...)' do
320
+ it 'populates the cache' do
321
+ Story.find(:all, :conditions => { :title => @story.title })
322
+ Story.fetch("title/#{@story.title}").should == [@story.id]
323
+ end
324
+ end
325
+
326
+ describe '#find(1)' do
327
+ it 'populates the cache' do
328
+ Story.find(@story.id)
329
+ Story.fetch("id/#{@story.id}").should == [@story]
330
+ end
331
+ end
332
+
333
+ end
334
+ end
335
+ end
336
+ end