simply_stored 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,470 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchHasManyTest < Test::Unit::TestCase
5
+ context "has_many" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ context "with has_many" do
12
+ should "create a fetch method for the associated objects" do
13
+ user = User.new
14
+ assert user.respond_to?(:posts)
15
+ end
16
+
17
+ should "fetch the associated objects" do
18
+ user = User.create(:title => "Mr.")
19
+ 3.times {
20
+ post = Post.new
21
+ post.user = user
22
+ post.save!
23
+ }
24
+ assert_equal 3, user.posts.size
25
+ end
26
+
27
+ should "set the parent object on the clients cache" do
28
+ User.expects(:find).never
29
+ user = User.create(:title => "Mr.")
30
+ 3.times {
31
+ post = Post.new
32
+ post.user = user
33
+ post.save!
34
+ }
35
+ post = user.posts.first
36
+ assert_equal user, user.posts.first.user
37
+ end
38
+
39
+ context "limit" do
40
+
41
+ should "be able to limit the result set" do
42
+ user = User.create(:title => "Mr.")
43
+ 3.times {
44
+ post = Post.new
45
+ post.user = user
46
+ post.save!
47
+ }
48
+ assert_equal 2, user.posts(:limit => 2).size
49
+ end
50
+
51
+ should "use the given options in the cache-key" do
52
+ user = User.create(:title => "Mr.")
53
+ 3.times {
54
+ post = Post.new
55
+ post.user = user
56
+ post.save!
57
+ }
58
+ assert_equal 2, user.posts(:limit => 2).size
59
+ assert_equal 3, user.posts(:limit => 3).size
60
+ end
61
+
62
+ should "be able to limit the result set - also for through objects" do
63
+ @user = User.create(:title => "Mr.")
64
+ first_pain = Pain.create
65
+ frist_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => first_pain)
66
+ assert_equal [first_pain], @user.pains
67
+ second_pain = Pain.create
68
+ second_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => second_pain)
69
+ @user.reload
70
+ assert_equal 2, @user.pains.size
71
+ assert_equal 1, @user.pains(:limit => 1).size
72
+ end
73
+ end
74
+
75
+ context "order" do
76
+ setup do
77
+ @user = User.create(:title => "Mr.")
78
+ 3.times {
79
+ post = Post.new
80
+ post.user = @user
81
+ post.save!
82
+ }
83
+ end
84
+
85
+ should "support different order" do
86
+ assert_nothing_raised do
87
+ @user.posts(:order => :asc)
88
+ end
89
+
90
+ assert_nothing_raised do
91
+ @user.posts(:order => :desc)
92
+ end
93
+ end
94
+
95
+ should "reverse the order if :desc" do
96
+ assert_equal @user.posts(:order => :asc).map(&:id).reverse, @user.posts(:order => :desc).map(&:id)
97
+ end
98
+
99
+ should "work with the limit option" do
100
+ last_post = Post.create(:user => @user)
101
+ assert_not_equal @user.posts(:order => :asc, :limit => 3).map(&:id).reverse, @user.posts(:order => :desc, :limit => 3).map(&:id)
102
+ end
103
+ end
104
+
105
+ should "verify the given options for the accessor method" do
106
+ user = User.create(:title => "Mr.")
107
+ assert_raise(ArgumentError) do
108
+ user.posts(:foo => false)
109
+ end
110
+ end
111
+
112
+ should "verify the given options for the association defintion" do
113
+ assert_raise(ArgumentError) do
114
+ User.instance_eval do
115
+ has_many :foo, :bar => :do
116
+ end
117
+ end
118
+ end
119
+
120
+ should "only fetch objects of the correct type" do
121
+ user = User.create(:title => "Mr.")
122
+ post = Post.new
123
+ post.user = user
124
+ post.save!
125
+
126
+ comment = Comment.new
127
+ comment.user = user
128
+ comment.save!
129
+
130
+ assert_equal 1, user.posts.size
131
+ end
132
+
133
+ should "getter should user cache" do
134
+ user = User.create(:title => "Mr.")
135
+ post = Post.new
136
+ post.user = user
137
+ post.save!
138
+ user.posts
139
+ assert_equal [post], user.instance_variable_get("@posts")[:all]
140
+ end
141
+
142
+ should "add methods to handle associated objects" do
143
+ user = User.new(:title => "Mr.")
144
+ assert user.respond_to?(:add_post)
145
+ assert user.respond_to?(:remove_post)
146
+ assert user.respond_to?(:remove_all_posts)
147
+ end
148
+
149
+ should 'ignore the cache when requesting explicit reload' do
150
+ user = User.create(:title => "Mr.")
151
+ assert_equal [], user.posts
152
+ post = Post.new
153
+ post.user = user
154
+ post.save!
155
+ assert_equal [post], user.posts(:force_reload => true)
156
+ end
157
+
158
+ context "when adding items" do
159
+ should "add the item to the internal cache" do
160
+ daddy = User.new(:title => "Mr.")
161
+ item = Post.new
162
+ assert_equal [], daddy.posts
163
+ daddy.add_post(item)
164
+ assert_equal [item], daddy.posts
165
+ assert_equal [item], daddy.instance_variable_get("@posts")[:all]
166
+ end
167
+
168
+ should "raise an error when the added item is not an object of the expected class" do
169
+ user = User.new
170
+ assert_raise(ArgumentError, 'excepted Post got String') do
171
+ user.add_post('foo')
172
+ end
173
+ end
174
+
175
+ should "save the added item" do
176
+ post = Post.new
177
+ user = User.create(:title => "Mr.")
178
+ user.add_post(post)
179
+ assert !post.new_record?
180
+ end
181
+
182
+ should 'set the forein key on the added object' do
183
+ post = Post.new
184
+ user = User.create(:title => "Mr.")
185
+ user.add_post(post)
186
+ assert_equal user.id, post.user_id
187
+ end
188
+ end
189
+
190
+ context "when removing items" do
191
+ should "should unset the foreign key" do
192
+ user = User.create(:title => "Mr.")
193
+ post = Post.create(:user => user)
194
+
195
+ user.remove_post(post)
196
+ assert_nil post.user_id
197
+ end
198
+
199
+ should "remove the item from the cache" do
200
+ user = User.create(:title => "Mr.")
201
+ post = Post.create(:user => user)
202
+ assert user.posts.include?(post)
203
+ user.remove_post(post)
204
+ assert !user.posts.any?{|p| post.id == p.id}
205
+ assert_equal [], user.instance_variable_get("@posts")[:all]
206
+ end
207
+
208
+ should "save the removed item with the nullified foreign key" do
209
+ user = User.create(:title => "Mr.")
210
+ post = Post.create(:user => user)
211
+
212
+ user.remove_post(post)
213
+ post = Post.find(post.id)
214
+ assert_nil post.user_id
215
+ end
216
+
217
+ should 'raise an error when another object is the owner of the object to be removed' do
218
+ user = User.create(:title => "Mr.")
219
+ mrs = User.create(:title => "Mrs.")
220
+ post = Post.create(:user => user)
221
+ assert_raise(ArgumentError) do
222
+ mrs.remove_post(post)
223
+ end
224
+ end
225
+
226
+ should 'raise an error when the object is the wrong type' do
227
+ user = User.new
228
+ assert_raise(ArgumentError, 'excepted Post got String') do
229
+ user.remove_post('foo')
230
+ end
231
+ end
232
+
233
+ should "delete the object when dependent:destroy" do
234
+ Category.instance_eval do
235
+ has_many :tags, :dependent => :destroy
236
+ end
237
+
238
+ category = Category.create(:name => "food")
239
+ tag = Tag.create(:name => "food", :category => category)
240
+ assert !tag.new?
241
+ category.remove_tag(tag)
242
+
243
+ assert_equal [], Tag.find(:all)
244
+ end
245
+
246
+ should "not nullify or delete dependents if the options is set to :ignore when removing" do
247
+ master = Master.create
248
+ master_id = master.id
249
+ servant = Servant.create(:master => master)
250
+ master.remove_servant(servant)
251
+ assert_equal master_id, servant.reload.master_id
252
+ end
253
+
254
+ should "not nullify or delete dependents if the options is set to :ignore when deleting" do
255
+ master = Master.create
256
+ master_id = master.id
257
+ servant = Servant.create(:master => master)
258
+ master.destroy
259
+ assert_equal master_id, servant.reload.master_id
260
+ end
261
+
262
+ end
263
+
264
+ context "when removing all items" do
265
+ should 'nullify the foreign keys on all referenced items' do
266
+ user = User.create(:title => "Mr.")
267
+ post = Post.create(:user => user)
268
+ post2 = Post.create(:user => user)
269
+ user.remove_all_posts
270
+ post = Post.find(post.id)
271
+ post2 = Post.find(post2.id)
272
+ assert_nil post.user_id
273
+ assert_nil post2.user_id
274
+ end
275
+
276
+ should 'empty the cache' do
277
+ user = User.create(:title => "Mr.")
278
+ post = Post.create(:user => user)
279
+ post2 = Post.create(:user => user)
280
+ user.remove_all_posts
281
+ assert_equal [], user.posts
282
+ assert_equal [], user.instance_variable_get("@posts")[:all]
283
+ end
284
+
285
+ context "when counting" do
286
+ setup do
287
+ @user = User.create(:title => "Mr.")
288
+ end
289
+
290
+ should "define a count method" do
291
+ assert @user.respond_to?(:post_count)
292
+ end
293
+
294
+ should "cache the result" do
295
+ assert_equal 0, @user.post_count
296
+ Post.create(:user => @user)
297
+ assert_equal 0, @user.post_count
298
+ assert_equal 0, @user.instance_variable_get("@post_count")
299
+ @user.instance_variable_set("@post_count", nil)
300
+ assert_equal 1, @user.post_count
301
+ end
302
+
303
+ should "force reload even if cached" do
304
+ assert_equal 0, @user.post_count
305
+ Post.create(:user => @user)
306
+ assert_equal 0, @user.post_count
307
+ assert_equal 1, @user.post_count(:force_reload => true)
308
+ end
309
+
310
+ should "count the number of belongs_to objects" do
311
+ assert_equal 0, @user.post_count(:force_reload => true)
312
+ Post.create(:user => @user)
313
+ assert_equal 1, @user.post_count(:force_reload => true)
314
+ Post.create(:user => @user)
315
+ assert_equal 2, @user.post_count(:force_reload => true)
316
+ end
317
+
318
+ should "not count foreign objects" do
319
+ assert_equal 0, @user.post_count
320
+ Post.create(:user => nil)
321
+ Post.create(:user => User.create(:title => 'Doc'))
322
+ assert_equal 0, @user.post_count
323
+ assert_equal 2, Post.count
324
+ end
325
+
326
+ should "not count delete objects" do
327
+ hemorrhoid = Hemorrhoid.create(:user => @user)
328
+ assert_equal 1, @user.hemorrhoid_count
329
+ hemorrhoid.delete
330
+ assert_equal 0, @user.hemorrhoid_count(:force_reload => true)
331
+ assert_equal 1, @user.hemorrhoid_count(:force_reload => true, :with_deleted => true)
332
+ end
333
+
334
+ should "work with has_many :through" do
335
+ assert_equal 0, @user.pain_count
336
+ first_pain = Pain.create
337
+ frist_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => first_pain)
338
+ assert_equal [first_pain], @user.pains
339
+ assert_equal 1, @user.pain_count(:force_reload => true)
340
+
341
+ second_pain = Pain.create
342
+ second_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => second_pain)
343
+ assert_equal 2, @user.pain_count(:force_reload => true)
344
+ end
345
+
346
+ end
347
+ end
348
+
349
+ context 'when destroying the parent objects' do
350
+ should "delete relations when dependent is destroy" do
351
+ Category.instance_eval do
352
+ has_many :tags, :dependent => :destroy
353
+ end
354
+
355
+ category = Category.create(:name => "food")
356
+ tag = Tag.create(:name => "food", :category => category)
357
+
358
+ assert_equal [tag], Tag.find(:all)
359
+ category.destroy
360
+ assert_equal [], Tag.find(:all)
361
+ end
362
+
363
+ should "nullify relations when dependent is nullify" do
364
+
365
+ user = User.create(:title => "Mr.")
366
+ post = Post.create(:user => user)
367
+
368
+ user.destroy
369
+ post = Post.find(post.id)
370
+ assert_nil post.user_id
371
+ end
372
+
373
+ should "nullify the foreign key even if validation forbids" do
374
+ user = User.create(:title => "Mr.")
375
+ post = StrictPost.create(:user => user)
376
+
377
+ user.destroy
378
+ post = StrictPost.find(post.id)
379
+ assert_nil post.user_id
380
+ end
381
+ end
382
+ end
383
+
384
+ context "with has_many :trough" do
385
+ setup do
386
+ @journal_1 = Journal.create
387
+ @journal_2 = Journal.create
388
+ @reader_1 = Reader.create
389
+ @reader_2 = Reader.create
390
+ end
391
+
392
+ should "raise an exception if there is no :through relation" do
393
+
394
+ assert_raise(ArgumentError) do
395
+ class FooHasManyThroughBar
396
+ include SimplyStored::Couch
397
+ has_many :foos, :through => :bars
398
+ end
399
+ end
400
+ end
401
+
402
+ should "define a getter" do
403
+ assert @journal_1.respond_to?(:readers)
404
+ assert @reader_1.respond_to?(:journals)
405
+ end
406
+
407
+ should "load the objects through" do
408
+ membership = Membership.new
409
+ membership.journal = @journal_1
410
+ membership.reader = @reader_1
411
+ assert membership.save
412
+
413
+ assert_equal @journal_1, membership.journal
414
+ assert_equal @reader_1, membership.reader
415
+ assert_equal [membership], @journal_1.reload.memberships
416
+ assert_equal [membership], @journal_1.reload.memberships
417
+
418
+ assert_equal [@reader_1], @journal_1.readers
419
+ assert_equal [@journal_1], @reader_1.journals
420
+
421
+ membership_2 = Membership.new
422
+ membership_2.journal = @journal_1
423
+ membership_2.reader = @reader_2
424
+ assert membership_2.save
425
+
426
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
427
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
428
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
429
+
430
+ membership_3 = Membership.new
431
+ membership_3.journal = @journal_2
432
+ membership_3.reader = @reader_2
433
+ assert membership_3.save
434
+
435
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
436
+ assert_equal [@reader_2.id].sort, @journal_2.reload.readers.map(&:id).sort
437
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
438
+ assert_equal [@journal_1.id, @journal_2.id].sort, @reader_2.reload.journals.map(&:id).sort
439
+
440
+ membership_3.destroy
441
+
442
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
443
+ assert_equal [], @journal_2.reload.readers
444
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
445
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
446
+ end
447
+
448
+ should "verify the given options" do
449
+ assert_raise(ArgumentError) do
450
+ @journal_1.readers(:foo => true)
451
+ end
452
+ end
453
+
454
+ should "not try to destroy/nullify through-objects on parent object delete" do
455
+ membership = Membership.new
456
+ membership.journal = @journal_1
457
+ membership.reader = @reader_1
458
+ assert membership.save
459
+
460
+ @reader_1.reload
461
+ @journal_1.reload
462
+
463
+ Reader.any_instance.expects("journal=").never
464
+ Journal.any_instance.expects(:readers).never
465
+
466
+ @journal_1.delete
467
+ end
468
+ end
469
+ end
470
+ end
@@ -0,0 +1,135 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchHasOneTest < Test::Unit::TestCase
5
+ context "with has_one" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ should "add a getter method" do
12
+ assert Instance.new.respond_to?(:identity)
13
+ end
14
+
15
+ should "fetch the object when invoking the getter" do
16
+ instance = Instance.create
17
+ identity = Identity.create(:instance => instance)
18
+ assert_equal identity, instance.identity
19
+ end
20
+
21
+ should "set the parent object on the clients cache" do
22
+ Instance.expects(:find).never
23
+ instance = Instance.create
24
+ identity = Identity.create(:instance => instance)
25
+
26
+ assert_equal instance, instance.identity.instance
27
+ end
28
+
29
+ should "verify the given options for the accessor method" do
30
+ instance = Instance.create
31
+ assert_raise(ArgumentError) do
32
+ instance.identity(:foo => :var)
33
+ end
34
+ end
35
+
36
+ should "verify the given options for the association defintion" do
37
+ assert_raise(ArgumentError) do
38
+ User.instance_eval do
39
+ has_one :foo, :bar => :do
40
+ end
41
+ end
42
+ end
43
+
44
+ should "store the fetched object into the cache" do
45
+ instance = Instance.create
46
+ identity = Identity.create(:instance => instance)
47
+ instance.identity
48
+ assert_equal identity, instance.instance_variable_get("@identity")
49
+ end
50
+
51
+ should "not fetch from the database when object is in cache" do
52
+ instance = Instance.create
53
+ identity = Identity.create(:instance => instance)
54
+ instance.identity
55
+ CouchPotato.database.expects(:view).never
56
+ instance.identity
57
+ end
58
+
59
+ should "update the foreign object to have the owner's id in the forein key" do
60
+ instance = Instance.create
61
+ identity = Identity.create
62
+ instance.identity = identity
63
+ identity.reload
64
+ assert_equal instance.id, identity.instance_id
65
+ end
66
+
67
+ should "update the cache when setting" do
68
+ instance = Instance.create
69
+ identity = Identity.create
70
+ instance.identity = identity
71
+ CouchPotato.expects(:database).never
72
+ assert_equal identity, instance.identity
73
+ end
74
+
75
+ should "set the foreign key value to nil when assigning nil" do
76
+ instance = Instance.create
77
+ identity = Identity.create(:instance => instance)
78
+ instance.identity = nil
79
+ identity = Identity.find(identity.id)
80
+ assert_nil identity.instance_id
81
+ end
82
+
83
+ should 'check the class' do
84
+ instance = Instance.create
85
+ assert_raise(ArgumentError, 'expected Item got String') do
86
+ instance.identity = 'foo'
87
+ end
88
+ end
89
+
90
+ should 'delete the dependent objects when dependent is set to destroy' do
91
+ identity = Identity.create
92
+ mag = Magazine.create
93
+ mag.identity = identity
94
+ mag.identity = nil
95
+ assert_nil Identity.find_by_id(identity.id)
96
+ end
97
+
98
+ should 'unset the id on the foreign object when a new object is set' do
99
+ instance = Instance.create
100
+ identity = Identity.create(:instance => instance)
101
+ identity2 = Identity.create
102
+
103
+ instance.identity = identity2
104
+ identity = Identity.find(identity.id)
105
+ assert_nil identity.instance_id
106
+ end
107
+
108
+ should 'delete the foreign object when a new object is set and dependent is set to destroy' do
109
+ identity = Identity.create
110
+ identity2 = Identity.create
111
+ mag = Magazine.create
112
+ mag.identity = identity
113
+ mag.identity = identity2
114
+ assert_nil Identity.find_by_id(identity.id)
115
+ end
116
+
117
+ should 'delete the foreign object when parent is destroyed and dependent is set to destroy' do
118
+ identity = Identity.create
119
+ mag = Magazine.create
120
+ mag.identity = identity
121
+
122
+ mag.destroy
123
+ assert_nil Identity.find_by_id(identity.id)
124
+ end
125
+
126
+ should 'nullify the foreign objects foreign key when parent is destroyed' do
127
+ identity = Identity.create
128
+ instance = Instance.create
129
+ instance.identity = identity
130
+ instance.destroy
131
+ identity = Identity.find(identity.id)
132
+ assert_nil identity.instance_id
133
+ end
134
+ end
135
+ end