spqr 0.0.1
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +202 -0
- data/README.rdoc +28 -0
- data/Rakefile +105 -0
- data/TODO +33 -0
- data/VERSION +1 -0
- data/bin/spqr-gen.rb +60 -0
- data/examples/codegen-schema.xml +7 -0
- data/examples/codegen/EchoAgent.rb +33 -0
- data/examples/hello.rb +44 -0
- data/examples/logservice.rb +90 -0
- data/lib/rhubarb/rhubarb.rb +504 -0
- data/lib/spqr/app.rb +299 -0
- data/lib/spqr/codegen.rb +434 -0
- data/lib/spqr/constants.rb +64 -0
- data/lib/spqr/manageable.rb +222 -0
- data/lib/spqr/spqr.rb +16 -0
- data/lib/spqr/utils.rb +88 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/spqr_spec.rb +5 -0
- data/spqr.spec.in +95 -0
- data/test/helper.rb +11 -0
- data/test/test_rhubarb.rb +608 -0
- data/test/test_spqr.rb +7 -0
- metadata +97 -0
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
|