spqr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/helper.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'spqr/spqr'
7
+ require 'spqr/app'
8
+ require 'rhubarb/rhubarb'
9
+
10
+ class Test::Unit::TestCase
11
+ end
@@ -0,0 +1,608 @@
1
+ # Test cases for Rhubarb, which is is a simple persistence layer for
2
+ # Ruby objects and SQLite.
3
+ #
4
+ # Copyright (c) 2009 Red Hat, Inc.
5
+ #
6
+ # Author: William Benton (willb@redhat.com)
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+
14
+ require 'rubygems'
15
+ require 'rhubarb/rhubarb'
16
+ require 'test/unit'
17
+
18
+ class TestClass
19
+ include Rhubarb::Persisting
20
+ declare_column :foo, :integer
21
+ declare_column :bar, :string
22
+ end
23
+
24
+ class TestClass2
25
+ include Rhubarb::Persisting
26
+ declare_column :fred, :integer
27
+ declare_column :barney, :string
28
+ declare_index_on :fred
29
+ end
30
+
31
+ class TC3
32
+ include Rhubarb::Persisting
33
+ declare_column :ugh, :datetime
34
+ declare_column :yikes, :integer
35
+ declare_constraint :yikes_pos, check("yikes >= 0")
36
+ end
37
+
38
+ class TC4
39
+ include Rhubarb::Persisting
40
+ declare_column :t1, :integer, references(TestClass)
41
+ declare_column :t2, :integer, references(TestClass2)
42
+ declare_column :enabled, :boolean, :default, :true
43
+ end
44
+
45
+ class SelfRef
46
+ include Rhubarb::Persisting
47
+ declare_column :one, :integer, references(SelfRef)
48
+ end
49
+
50
+ class CustomQueryTable
51
+ include Rhubarb::Persisting
52
+ declare_column :one, :integer
53
+ declare_column :two, :integer
54
+ declare_query :ltcols, "one < two"
55
+ declare_query :ltvars, "one < ? and two < ?"
56
+ declare_custom_query :cltvars, "select * from __TABLE__ where one < ? and two < ?"
57
+ end
58
+
59
+ class ToRef
60
+ include Rhubarb::Persisting
61
+ declare_column :foo, :string
62
+ end
63
+
64
+ class FromRef
65
+ include Rhubarb::Persisting
66
+ declare_column :t, :integer, references(ToRef, :on_delete=>:cascade)
67
+ end
68
+
69
+ class FreshTestTable
70
+ include Rhubarb::Persisting
71
+ declare_column :fee, :integer
72
+ declare_column :fie, :integer
73
+ declare_column :foe, :integer
74
+ declare_column :fum, :integer
75
+ end
76
+
77
+ class BackendBasicTests < Test::Unit::TestCase
78
+ def setup
79
+ Rhubarb::Persistence::open(":memory:")
80
+ klasses = []
81
+ klasses << TestClass
82
+ klasses << TestClass2
83
+ klasses << TC3
84
+ klasses << TC4
85
+ klasses << SelfRef
86
+ klasses << CustomQueryTable
87
+ klasses << ToRef
88
+ klasses << FromRef
89
+ klasses << FreshTestTable
90
+
91
+ klasses.each { |klass| klass.create_table }
92
+
93
+ @flist = []
94
+ end
95
+
96
+ def teardown
97
+ Rhubarb::Persistence::close()
98
+ end
99
+
100
+ def test_persistence_setup
101
+ assert Rhubarb::Persistence::db.type_translation, "type translation not enabled for db"
102
+ assert Rhubarb::Persistence::db.results_as_hash, "rows-as-hashes not enabled for db"
103
+ end
104
+
105
+ def test_reference_ctor_klass
106
+ r = Rhubarb::Reference.new(TestClass)
107
+ assert(r.referent == TestClass, "Referent of managed reference instance incorrect")
108
+ assert(r.column == "row_id", "Column of managed reference instance incorrect")
109
+ assert(r.to_s == "references TestClass(row_id)", "string representation of managed reference instance incorrect")
110
+ assert(r.managed_ref?, "managed reference should return true for managed_ref?")
111
+ end
112
+
113
+ def test_reference_ctor_string
114
+ r = Rhubarb::Reference.new("TestClass")
115
+ assert(r.referent == "TestClass", "Referent of string-backed reference instance incorrect")
116
+ assert(r.column == "row_id", "Column of string-backed reference instance incorrect")
117
+ assert(r.to_s == "references TestClass(row_id)", "string representation of string-backed reference instance incorrect")
118
+ assert(r.managed_ref? == false, "unmanaged reference should return false for managed_ref?")
119
+ end
120
+
121
+ def test_instance_methods
122
+ ["foo", "bar"].each do |prefix|
123
+ ["#{prefix}", "#{prefix}="].each do |m|
124
+ assert TestClass.instance_methods.include?(m), "#{m} method not declared in TestClass"
125
+ end
126
+ end
127
+ end
128
+
129
+ def test_instance_methods2
130
+ ["fred", "barney"].each do |prefix|
131
+ ["#{prefix}", "#{prefix}="].each do |m|
132
+ assert TestClass2.instance_methods.include?(m), "#{m} method not declared in TestClass2"
133
+ end
134
+ end
135
+ end
136
+
137
+ def test_instance_methods_neg
138
+ ["fred", "barney"].each do |prefix|
139
+ ["#{prefix}", "#{prefix}="].each do |m|
140
+ bogus_include = TestClass.instance_methods.include? m
141
+ assert(bogus_include == false, "#{m} method declared in TestClass; shouldn't be")
142
+ end
143
+ end
144
+ end
145
+
146
+ def test_instance_methods_dont_include_class_methods
147
+ ["foo", "bar"].each do |prefix|
148
+ ["find_by_#{prefix}", "find_first_by_#{prefix}"].each do |m|
149
+ bogus_include = TestClass.instance_methods.include? m
150
+ assert(bogus_include == false, "#{m} method declared in TestClass; shouldn't be")
151
+ end
152
+ end
153
+ end
154
+
155
+ def test_class_methods
156
+ ["foo", "bar"].each do |prefix|
157
+ ["find_by_#{prefix}", "find_first_by_#{prefix}"].each do |m|
158
+ klass = class << TestClass; self end
159
+ assert klass.instance_methods.include?(m), "#{m} method not declared in TestClass' eigenclass"
160
+ end
161
+ end
162
+ end
163
+
164
+ def test_class_methods2
165
+ ["fred", "barney"].each do |prefix|
166
+ ["find_by_#{prefix}", "find_first_by_#{prefix}"].each do |m|
167
+ klass = class << TestClass2; self end
168
+ assert klass.instance_methods.include?(m), "#{m} method not declared in TestClass2's eigenclass"
169
+ end
170
+ end
171
+ end
172
+
173
+ def test_table_class_methods_neg
174
+ ["foo", "bar", "fred", "barney"].each do |prefix|
175
+ ["find_by_#{prefix}", "find_first_by_#{prefix}"].each do |m|
176
+ klass = Class.new
177
+
178
+ klass.class_eval do
179
+ include Rhubarb::Persisting
180
+ end
181
+
182
+ bogus_include = klass.instance_methods.include?(m)
183
+ assert(bogus_include == false, "#{m} method declared in eigenclass of class including Rhubarb::Persisting; shouldn't be")
184
+ end
185
+ end
186
+ end
187
+
188
+ def test_class_methods_neg
189
+ ["fred", "barney"].each do |prefix|
190
+ ["find_by_#{prefix}", "find_first_by_#{prefix}"].each do |m|
191
+ klass = class << TestClass; self end
192
+ bogus_include = klass.instance_methods.include?(m)
193
+ assert(bogus_include == false, "#{m} method declared in TestClass' eigenclass; shouldn't be")
194
+ end
195
+ end
196
+ end
197
+
198
+ def test_column_size
199
+ assert(TestClass.columns.size == 5, "TestClass has wrong number of columns")
200
+ end
201
+
202
+ def test_tc2_column_size
203
+ assert(TestClass2.columns.size == 5, "TestClass2 has wrong number of columns")
204
+ end
205
+
206
+ def test_table_column_size
207
+ klass = Class.new
208
+ klass.class_eval do
209
+ include Rhubarb::Persisting
210
+ end
211
+ if klass.respond_to? :columns
212
+ assert(Frotz.columns.size == 0, "A persisting class with no declared columns has the wrong number of columns")
213
+ end
214
+ end
215
+
216
+ def test_constraints_size
217
+ k = Class.new
218
+
219
+ k.class_eval do
220
+ include Rhubarb::Persisting
221
+ end
222
+
223
+ {k => 0, TestClass => 0, TestClass2 => 0, TC3 => 1}.each do |klass, cts|
224
+ if klass.respond_to? :constraints
225
+ assert(klass.constraints.size == cts, "#{klass} has wrong number of constraints")
226
+ end
227
+ end
228
+ end
229
+
230
+ def test_cols_and_constraints_understood
231
+ [TestClass, TestClass2, TC3, TC4].each do |klass|
232
+ assert(klass.respond_to?(:constraints), "#{klass} should have accessor for constraints")
233
+ assert(klass.respond_to?(:columns), "#{klass} should have accessor for columns")
234
+ end
235
+ end
236
+
237
+ def test_column_contents
238
+ [:row_id, :foo, :bar].each do |col|
239
+ assert(TestClass.columns.map{|c| c.name}.include?(col), "TestClass doesn't contain column #{col}")
240
+ end
241
+ end
242
+
243
+ def test_create_proper_type
244
+ tc = TestClass.create(:foo => 1, :bar => "argh")
245
+ assert(tc.class == TestClass, "TestClass.create should return an instance of TestClass")
246
+ end
247
+
248
+ def test_create_multiples
249
+ tc_list = [nil]
250
+ 1.upto(9) do |num|
251
+ tc_list.push TestClass.create(:foo => num, :bar => "argh#{num}")
252
+ end
253
+
254
+ 1.upto(9) do |num|
255
+ assert(tc_list[num].foo == num, "multiple TestClass.create invocations should return records with proper foo values")
256
+ assert(tc_list[num].bar == "argh#{num}", "multiple TestClass.create invocations should return records with proper bar values")
257
+
258
+ tmp = TestClass.find(num)
259
+
260
+ assert(tmp.foo == num, "multiple TestClass.create invocations should add records with proper foo values to the db")
261
+ assert(tmp.bar == "argh#{num}", "multiple TestClass.create invocations should add records with proper bar values to the db")
262
+ end
263
+ end
264
+
265
+ def test_delete
266
+ range = []
267
+ 1.upto(9) do |num|
268
+ range << num
269
+ TestClass.create(:foo => num, :bar => "argh#{num}")
270
+ end
271
+
272
+ assert(TestClass.count == range.size, "correct number of rows inserted prior to delete")
273
+
274
+ TestClass.find(2).delete
275
+
276
+ assert(TestClass.count == range.size - 1, "correct number of rows inserted after delete")
277
+ end
278
+
279
+ def test_delete_2
280
+ TestClass.create(:foo => 42, :bar => "Wait, what was the question?")
281
+ tc1 = TestClass.find_first_by_foo(42)
282
+ tc2 = TestClass.find_first_by_foo(42)
283
+
284
+ [tc1,tc2].each do |obj|
285
+ assert obj
286
+ assert_kind_of TestClass, obj
287
+ assert_equal false, obj.instance_eval {needs_refresh?}
288
+ end
289
+
290
+ [:foo, :bar, :row_id].each do |msg|
291
+ assert_equal tc1.send(msg), tc2.send(msg)
292
+ end
293
+
294
+ tc1.delete
295
+
296
+ tc3 = TestClass.find_by_foo(42)
297
+ assert_equal [], tc3
298
+
299
+ tc3 = TestClass.find_first_by_foo(42)
300
+ assert_equal nil, tc3
301
+
302
+ [tc1, tc2].each do |obj|
303
+ assert obj.deleted?
304
+ [:foo, :bar, :row_id].each do |msg|
305
+ assert_equal nil, obj.send(msg)
306
+ end
307
+ end
308
+ end
309
+
310
+ def test_count_base
311
+ assert(TestClass.count == 0, "a new table should have no rows")
312
+ end
313
+
314
+ def test_count_inc
315
+ 1.upto(9) do |num|
316
+ TestClass.create(:foo => num, :bar => "argh#{num}")
317
+ assert(TestClass.count == num, "table row count should increment after each row create")
318
+ end
319
+ end
320
+
321
+ def test_create_proper_values
322
+ vals = {:foo => 1, :bar => "argh"}
323
+ tc = TestClass.create(vals)
324
+ assert(tc.foo == 1, "tc.foo (newly-created) should have the value 1")
325
+ assert(tc.bar == "argh", "tc.bar (newly-created) should have the value \"argh\"")
326
+ end
327
+
328
+ def test_create_and_find_by_id
329
+ vals = {:foo => 2, :bar => "argh"}
330
+ TestClass.create(vals)
331
+
332
+ tc = TestClass.find(1)
333
+ assert(tc.foo == 2, "tc.foo (found by id) should have the value 2")
334
+ assert(tc.bar == "argh", "tc.bar (found by id) should have the value \"argh\"")
335
+ end
336
+
337
+ def test_find_by_id_bogus
338
+ tc = TestClass.find(1)
339
+ assert(tc == nil, "TestClass table should be empty")
340
+ end
341
+
342
+ def test_create_and_find_by_foo
343
+ vals = {:foo => 2, :bar => "argh"}
344
+ TestClass.create(vals)
345
+
346
+ result = TestClass.find_by_foo(2)
347
+ tc = result[0]
348
+ assert(result.size == 1, "TestClass.find_by_foo(2) should return exactly one result")
349
+ assert(tc.foo == 2, "tc.foo (found by foo) should have the value 2")
350
+ assert(tc.bar == "argh", "tc.bar (found by foo) should have the value \"argh\"")
351
+ end
352
+
353
+ def test_create_and_find_first_by_foo
354
+ vals = {:foo => 2, :bar => "argh"}
355
+ TestClass.create(vals)
356
+
357
+ tc = (TestClass.find_first_by_foo(2))
358
+ assert(tc.foo == 2, "tc.foo (found by foo) should have the value 2")
359
+ assert(tc.bar == "argh", "tc.bar (found by foo) should have the value \"argh\"")
360
+ end
361
+
362
+ def test_create_and_find_by_bar
363
+ vals = {:foo => 2, :bar => "argh"}
364
+ TestClass.create(vals)
365
+ result = TestClass.find_by_bar("argh")
366
+ tc = result[0]
367
+ assert(result.size == 1, "TestClass.find_by_bar(\"argh\") should return exactly one result")
368
+ assert(tc.foo == 2, "tc.foo (found by bar) should have the value 2")
369
+ assert(tc.bar == "argh", "tc.bar (found by bar) should have the value \"argh\"")
370
+ end
371
+
372
+ def test_create_and_find_first_by_bar
373
+ vals = {:foo => 2, :bar => "argh"}
374
+ TestClass.create(vals)
375
+
376
+ tc = (TestClass.find_first_by_bar("argh"))
377
+ assert(tc.foo == 2, "tc.foo (found by bar) should have the value 2")
378
+ assert(tc.bar == "argh", "tc.bar (found by bar) should have the value \"argh\"")
379
+ end
380
+
381
+ def test_create_and_update_modifies_object
382
+ vals = {:foo => 1, :bar => "argh"}
383
+ TestClass.create(vals)
384
+
385
+ tc = TestClass.find(1)
386
+ tc.foo = 2
387
+ assert("#{tc.foo}" == "2", "tc.foo should have the value 2 after modifying object")
388
+ end
389
+
390
+ def test_create_and_update_modifies_db
391
+ vals = {:foo => 1, :bar => "argh"}
392
+ TestClass.create(vals)
393
+
394
+ tc = TestClass.find(1)
395
+ tc.foo = 2
396
+
397
+ tc_fresh = TestClass.find(1)
398
+ assert(tc_fresh.foo == 2, "foo value in first row of db should have the value 2 after modifying tc object")
399
+ end
400
+
401
+ def test_create_and_update_freshen
402
+ vals = {:foo => 1, :bar => "argh"}
403
+ TestClass.create(vals)
404
+
405
+ tc_fresh = TestClass.find(1)
406
+ tc = TestClass.find(1)
407
+
408
+ tc.foo = 2
409
+
410
+ assert(tc_fresh.foo == 2, "object backed by db row isn't freshened")
411
+ end
412
+
413
+ def test_reference_tables
414
+ assert(TC4.refs.size == 2, "TC4 should have 2 refs, instead has #{TC4.refs.size}")
415
+ end
416
+
417
+ def test_reference_classes
418
+ t_vals = []
419
+ t2_vals = []
420
+
421
+ 1.upto(9) do |n|
422
+ t_vals.push({:foo => n, :bar => "item-#{n}"})
423
+ TestClass.create t_vals[-1]
424
+ end
425
+
426
+ 9.downto(1) do |n|
427
+ t2_vals.push({:fred => n, :barney => "barney #{n}"})
428
+ TestClass2.create t2_vals[-1]
429
+ end
430
+
431
+ 1.upto(9) do |n|
432
+ m = 10-n
433
+ k = TC4.create(:t1 => n, :t2 => m)
434
+ assert(k.t1.class == TestClass, "k.t1.class is #{k.t1.class}; should be TestClass")
435
+ assert(k.t2.class == TestClass2, "k.t2.class is #{k.t2.class}; should be TestClass2")
436
+ assert(k.enabled)
437
+ k.enabled = false
438
+ assert(k.enabled==false)
439
+ end
440
+ end
441
+
442
+ def test_references_simple
443
+ t_vals = []
444
+ t2_vals = []
445
+
446
+ 1.upto(9) do |n|
447
+ t_vals.push({:foo => n, :bar => "item-#{n}"})
448
+ TestClass.create t_vals[-1]
449
+ end
450
+
451
+ 9.downto(1) do |n|
452
+ t2_vals.push({:fred => n, :barney => "barney #{n}"})
453
+ TestClass2.create t2_vals[-1]
454
+ end
455
+
456
+ 1.upto(9) do |n|
457
+ k = TC4.create(:t1 => n, :t2 => (10 - n))
458
+ assert(k.t1.foo == k.t2.fred, "references don't work")
459
+ end
460
+ end
461
+
462
+ def test_references_sameclass
463
+ SelfRef.create :one => nil
464
+ 1.upto(3) do |num|
465
+ SelfRef.create :one => num
466
+ end
467
+ 4.downto(2) do |num|
468
+ sr = SelfRef.find num
469
+ assert(sr.one.class == SelfRef, "SelfRef with row ID #{num} should have a one field of type SelfRef; is #{sr.one.class} instead")
470
+ assert(sr.one.row_id == sr.row_id - 1, "SelfRef with row ID #{num} should have a one field with a row id of #{sr.row_id - 1}; is #{sr.one.row_id} instead")
471
+ end
472
+ end
473
+
474
+ def test_references_circular_id
475
+ sr = SelfRef.create :one => nil
476
+ sr.one = sr.row_id
477
+ assert(sr == sr.one, "self-referential rows should work; instead #{sr} isn't the same as #{sr.one}")
478
+ end
479
+
480
+ def test_references_circular_obj
481
+ sr = SelfRef.create :one => nil
482
+ sr.one = sr
483
+ assert(sr == sr.one, "self-referential rows should work; instead #{sr} isn't the same as #{sr.one}")
484
+ end
485
+
486
+ def test_referential_integrity
487
+ assert_raise SQLite3::SQLException do
488
+ FromRef.create(:t => 42)
489
+ end
490
+
491
+ assert_nothing_thrown do
492
+ 1.upto(20) do |x|
493
+ ToRef.create(:foo => "#{x}")
494
+ FromRef.create(:t => x)
495
+ assert_equal ToRef.count, FromRef.count
496
+ end
497
+ end
498
+
499
+ 20.downto(1) do |x|
500
+ ct = ToRef.count
501
+ tr = ToRef.find(x)
502
+ tr.delete
503
+ assert_equal ToRef.count, ct - 1
504
+ assert_equal ToRef.count, FromRef.count
505
+ end
506
+ end
507
+
508
+ def test_custom_query
509
+ colresult = 0
510
+ varresult = 0
511
+
512
+ 1.upto(20) do |i|
513
+ 1.upto(20) do |j|
514
+ CustomQueryTable.create(:one => i, :two => j)
515
+ colresult = colresult.succ if i < j
516
+ varresult = varresult.succ if i < 5 && j < 7
517
+ end
518
+ end
519
+
520
+ f = CustomQueryTable.ltcols
521
+ assert(f.size() == colresult, "f.size() should equal colresult, but #{f.size()} != #{colresult}")
522
+ f.each {|r| assert(r.one < r.two, "#{r.one}, #{r.two} should only be in ltcols custom query if #{r.one} < #{r.two}") }
523
+
524
+ f = CustomQueryTable.ltvars 5, 7
525
+ f2 = CustomQueryTable.cltvars 5, 7
526
+
527
+ [f,f2].each do |obj|
528
+ assert(obj.size() == varresult, "query result size should equal varresult, but #{obj.size()} != #{varresult}")
529
+ obj.each {|r| assert(r.one < 5 && r.two < 7, "#{r.one}, #{r.two} should only be in ltvars/cltvars custom query if #{r.one} < 5 && #{r.two} < 7") }
530
+ end
531
+
532
+ end
533
+
534
+ def freshness_query_fixture
535
+ @flist = []
536
+
537
+ 0.upto(99) do |x|
538
+ @flist << FreshTestTable.create(:fee=>x, :fie=>(x%7), :foe=>(x%11), :fum=>(x%13))
539
+ end
540
+ end
541
+
542
+ def test_freshness_query_basic
543
+ freshness_query_fixture
544
+ # basic test
545
+ basic = FreshTestTable.find_freshest(:group_by=>[:fee])
546
+
547
+ assert_equal(@flist.size, basic.size)
548
+ 0.upto(99) do |x|
549
+ [:fee,:fie,:foe,:fum,:created,:updated,:row_id].each do |msg|
550
+ assert_equal(@flist[x].send(msg), basic[x].send(msg))
551
+ end
552
+ end
553
+ end
554
+
555
+ def test_freshness_query_basic_restricted
556
+ freshness_query_fixture
557
+ # basic test
558
+
559
+ basic = FreshTestTable.find_freshest(:group_by=>[:fee], :version=>@flist[30].created, :debug=>true)
560
+
561
+ assert_equal(31, basic.size)
562
+ 0.upto(30) do |x|
563
+ [:fee,:fie,:foe,:fum,:created,:updated,:row_id].each do |msg|
564
+ assert_equal(@flist[x].send(msg), basic[x].send(msg))
565
+ end
566
+ end
567
+ end
568
+
569
+ def test_freshness_query_basic_select
570
+ freshness_query_fixture
571
+ # basic test
572
+
573
+ basic = FreshTestTable.find_freshest(:group_by=>[:fee], :select_by=>{:fie=>0}, :debug=>true)
574
+
575
+ expected_ct = 99/7 + 1;
576
+
577
+ assert_equal(expected_ct, basic.size)
578
+
579
+ 0.upto(expected_ct - 1) do |x|
580
+ [:fee,:fie,:foe,:fum,:created,:updated,:row_id].each do |msg|
581
+ assert_equal(@flist[x*7].send(msg), basic[x].send(msg))
582
+ end
583
+ end
584
+ end
585
+
586
+ def test_freshness_query_group_single
587
+ freshness_query_fixture
588
+ # more basic tests
589
+ pairs = {:fie=>7,:foe=>11,:fum=>13}
590
+ pairs.each do |col,ct|
591
+ basic = FreshTestTable.find_freshest(:group_by=>[col])
592
+ assert_equal(ct,basic.size)
593
+
594
+ expected_objs = {}
595
+
596
+ 99.downto(99-ct+1) do |x|
597
+ expected_objs[x%ct] = @flist[x]
598
+ end
599
+
600
+ basic.each do |row|
601
+ res = expected_objs[row.send(col)]
602
+ [:fee,:fie,:foe,:fum,:created,:updated,:row_id].each do |msg|
603
+ assert_equal(res.send(msg), row.send(msg))
604
+ end
605
+ end
606
+ end
607
+ end
608
+ end