pod4 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,511 @@
1
+ #
2
+ # Supplemental test for Sequel -- tests using PG gem in MRI, jeremyevans-postgres-pg under jruby?
3
+ #
4
+
5
+ require 'pod4/sequel_interface'
6
+
7
+ require 'sequel'
8
+ require 'date'
9
+ require 'time'
10
+ require 'bigdecimal'
11
+
12
+
13
+ class TestSequelInterfacePg < SequelInterface
14
+ set_table :customer
15
+ set_id_fld :id
16
+ end
17
+
18
+ class SchemaSequelInterfacePg < SequelInterface
19
+ set_schema :public
20
+ set_table :customer
21
+ set_id_fld :id
22
+ end
23
+
24
+ class ProdSequelInterfacePg < SequelInterface
25
+ set_table :product
26
+ set_id_fld :code
27
+ end
28
+
29
+
30
+
31
+
32
+ describe TestSequelInterfacePg do
33
+
34
+ let(:data) do
35
+ d = []
36
+ d << { name: 'Barney',
37
+ level: 1.23,
38
+ day: Date.parse("2016-01-01"),
39
+ timestamp: Time.parse('2015-01-01 12:11'),
40
+ qty: BigDecimal.new("1.24"),
41
+ price: BigDecimal.new("1.24") }
42
+
43
+ d << { name: 'Fred',
44
+ level: 2.34,
45
+ day: Date.parse("2016-02-02"),
46
+ timestamp: Time.parse('2015-01-02 12:22'),
47
+ qty: BigDecimal.new("2.35"),
48
+ price: BigDecimal.new("2.35") }
49
+
50
+ d << { name: 'Betty',
51
+ level: 3.45,
52
+ day: Date.parse("2016-03-03"),
53
+ timestamp: Time.parse('2015-01-03 12:33'),
54
+ qty: BigDecimal.new("3.46"),
55
+ price: BigDecimal.new("3.46") }
56
+
57
+ d
58
+ end
59
+
60
+ def fill_data(ifce)
61
+ data.each{|r| ifce.create(r) }
62
+ end
63
+
64
+ def fill_product_data(ifce)
65
+ ifce.create( {code: "foo", name: "bar"} )
66
+ end
67
+
68
+
69
+ let (:db) do
70
+ db = Sequel.connect('postgres://pod4test:pod4test@centos7andy/pod4_test?search_path=public')
71
+
72
+ db.run %Q|
73
+ drop table if exists customer;
74
+ drop table if exists product;
75
+
76
+ create table customer (
77
+ id serial primary key,
78
+ name text,
79
+ level real null,
80
+ day date null,
81
+ timestamp timestamp null,
82
+ price money null,
83
+ qty numeric null );
84
+
85
+ create table product (
86
+ code text,
87
+ name text );|
88
+
89
+ db
90
+ end
91
+
92
+ let(:interface) { TestSequelInterfacePg.new(db) }
93
+ let(:prod_interface) { ProdSequelInterfacePg.new(db) }
94
+
95
+
96
+ before do
97
+ interface.execute %Q|
98
+ truncate table customer restart identity;
99
+ truncate table product;|
100
+
101
+ fill_data(interface)
102
+ end
103
+
104
+ after do
105
+ db.disconnect
106
+ end
107
+
108
+ ##
109
+
110
+
111
+ describe '#quoted_table' do
112
+
113
+ it 'returns just the table when the schema is not set' do
114
+ expect( interface.quoted_table.downcase ).to eq( %Q|"customer"| )
115
+ end
116
+
117
+ it 'returns the schema plus table when the schema is set' do
118
+ ifce = SchemaSequelInterfacePg.new(db)
119
+ expect( ifce.quoted_table.downcase ).to eq( %|"public"."customer"| )
120
+ end
121
+
122
+ end
123
+ ##
124
+
125
+
126
+ describe '#create' do
127
+
128
+ let(:hash) { {name: 'Bam-Bam', qty: 4.44} }
129
+ let(:ot) { Octothorpe.new(name: 'Wilma', qty: 5.55) }
130
+
131
+ it 'raises a Pod4::DatabaseError if anything goes wrong' do
132
+ expect{ interface.create(one: 'two') }.to raise_exception DatabaseError
133
+ end
134
+
135
+ it 'creates the record when given a hash' do
136
+ # kinda impossible to seperate these two tests
137
+ id = interface.create(hash)
138
+
139
+ expect( id ).not_to be_nil
140
+ expect{ interface.read(id) }.not_to raise_exception
141
+ expect( interface.read(id).to_h ).to include hash
142
+ end
143
+
144
+ it 'creates the record when given an Octothorpe' do
145
+ id = interface.create(ot)
146
+
147
+ expect{ interface.read(id) }.not_to raise_exception
148
+ expect( interface.read(id).to_h ).to include ot.to_h
149
+ end
150
+
151
+ it 'does not freak out if the hash has symbol values' do
152
+ # Which, Sequel does
153
+ expect{ interface.create(name: :Booboo) }.not_to raise_exception
154
+ end
155
+
156
+ it 'shouldnt have a problem with record values of nil' do
157
+ record = {name: 'Ranger', price: nil}
158
+ expect{ interface.create(record) }.not_to raise_exception
159
+ id = interface.create(record)
160
+ expect( interface.read(id).to_h ).to include(record)
161
+ end
162
+
163
+ it 'shouldnt have a problem with strings containing special characters' do
164
+ record = {name: "T'Challa[]", price: nil}
165
+ expect{ interface.create(record) }.not_to raise_exception
166
+ id = interface.create(record)
167
+ expect( interface.read(id).to_h ).to include(record)
168
+ end
169
+
170
+ it 'shouldn\'t have a problem with non-integer keys' do
171
+ hash = {code: "foo", name: "bar"}
172
+ id = prod_interface.create( Octothorpe.new(hash) )
173
+
174
+ expect( id ).to eq "foo"
175
+ expect{ prod_interface.read("foo") }.not_to raise_exception
176
+ expect( prod_interface.read("foo").to_h ).to include hash
177
+ end
178
+
179
+ end
180
+ ##
181
+
182
+
183
+ describe '#read' do
184
+
185
+ it 'returns the record for the id as an Octothorpe' do
186
+ expect( interface.read(2).to_h ).to include(name: 'Fred', qty: 2.35)
187
+ end
188
+
189
+ it 'raises a Pod4::CantContinue if the ID is bad' do
190
+ expect{ interface.read(:foo) }.to raise_exception CantContinue
191
+ end
192
+
193
+ it 'returns an empty Octothorpe if no record matches the ID' do
194
+ expect{ interface.read(99) }.not_to raise_exception
195
+ expect( interface.read(99) ).to be_a_kind_of Octothorpe
196
+ expect( interface.read(99) ).to be_empty
197
+ end
198
+
199
+ it 'returns real fields as Float' do
200
+ level = interface.read(1).>>.level
201
+
202
+ expect( level ).to be_a_kind_of Float
203
+ expect( level ).to be_within(0.001).of( data.first[:level] )
204
+ end
205
+
206
+ it 'returns date fields as Date' do
207
+ date = interface.read(1).>>.day
208
+
209
+ expect( date ).to be_a_kind_of Date
210
+ expect( date ).to eq data.first[:day]
211
+ end
212
+
213
+ it 'returns datetime fields as Time' do
214
+ timestamp = interface.read(1).>>.timestamp
215
+
216
+ expect( timestamp ).to be_a_kind_of Time
217
+ expect( timestamp ).to eq data.first[:timestamp]
218
+ end
219
+
220
+ it 'returns numeric fields as BigDecimal' do
221
+ qty = interface.read(1).>>.qty
222
+
223
+ expect( qty ).to be_a_kind_of BigDecimal
224
+ expect( qty ).to eq data.first[:qty]
225
+ end
226
+
227
+ it 'returns money fields as bigdecimal' do
228
+ pending "Sequel/PG returns a string for Money type"
229
+
230
+ price = interface.read(1).>>.price
231
+
232
+ expect( price ).to be_a_kind_of BigDecimal
233
+ expect( price ).to eq data.first[:price]
234
+ end
235
+
236
+ it 'shouldn\'t have a problem with non-integer keys' do
237
+ # this is a 100% overlap with the create test above...
238
+ fill_product_data(prod_interface)
239
+
240
+ expect{ prod_interface.read("foo") }.not_to raise_exception
241
+ expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
242
+ end
243
+
244
+ end
245
+ ##
246
+
247
+
248
+
249
+ describe '#list' do
250
+
251
+ it 'returns an array of Octothorpes that match the records' do
252
+ arr = interface.list.map {|ot| x = ot.to_h}
253
+
254
+ expect( arr.size ).to eq(data.size)
255
+
256
+ data.each do |d|
257
+ r = arr.find{|x| x[:name] == d[:name] }
258
+ expect( r ).not_to be_nil
259
+ expect( r[:level] ).to be_within(0.001).of( d[:level] )
260
+ expect( r[:day] ).to eq d[:day]
261
+ expect( r[:timestamp] ).to eq d[:timestamp]
262
+ expect( r[:qty] ).to eq d[:qty]
263
+ end
264
+
265
+ end
266
+
267
+ it 'returns a subset of records based on the selection parameter' do
268
+ expect( interface.list(name: 'Fred').size ).to eq 1
269
+
270
+ expect( interface.list(name: 'Betty').first.to_h ).
271
+ to include(name: 'Betty', qty: 3.46)
272
+
273
+ end
274
+
275
+ it 'returns an empty Array if nothing matches' do
276
+ expect( interface.list(name: 'Yogi') ).to eq([])
277
+ end
278
+
279
+ it 'raises DatabaseError if the selection criteria is nonsensical' do
280
+ expect{ interface.list('foo') }.to raise_exception Pod4::DatabaseError
281
+ end
282
+
283
+ it 'returns an empty array if there is no data' do
284
+ interface.list.each {|x| interface.delete(x[interface.id_fld]) }
285
+ expect( interface.list ).to eq([])
286
+ end
287
+
288
+ it 'does not freak out if the hash has symbol values' do
289
+ # Which, Sequel does
290
+ expect{ interface.list(name: :Barney) }.not_to raise_exception
291
+ end
292
+
293
+
294
+ end
295
+ ##
296
+
297
+
298
+ describe '#update' do
299
+
300
+ let(:id) { interface.list.first[:id] }
301
+
302
+ it 'updates the record at ID with record parameter' do
303
+ record = {name: 'Booboo', qty: 99.99}
304
+ interface.update(id, record)
305
+
306
+ booboo = interface.read(id)
307
+ expect( booboo.>>.name ).to eq( record[:name] )
308
+ expect( booboo.>>.qty.to_f ).to eq( record[:qty] )
309
+ end
310
+
311
+ it 'raises a CantContinue if anything weird happens with the ID' do
312
+ expect{ interface.update(99, name: 'Booboo') }.
313
+ to raise_exception CantContinue
314
+
315
+ end
316
+
317
+ it 'raises a DatabaseError if anything weird happensi with the record' do
318
+ expect{ interface.update(id, smarts: 'more') }.
319
+ to raise_exception DatabaseError
320
+
321
+ end
322
+
323
+ it 'does not freak out if the hash has symbol values' do
324
+ # Which, Sequel does
325
+ expect{ interface.update(id, name: :Booboo) }.not_to raise_exception
326
+ end
327
+
328
+ it 'shouldn\'t have a problem with record values of nil' do
329
+ record = {name: 'Ranger', qty: nil}
330
+ expect{ interface.update(id, record) }.not_to raise_exception
331
+ expect( interface.read(id).to_h ).to include(record)
332
+ end
333
+
334
+ it 'shouldn\'t have a problem with strings containing special characters' do
335
+ record = {name: "T'Challa[]", qty: nil}
336
+ expect{ interface.update(id, record) }.not_to raise_exception
337
+ expect( interface.read(id).to_h ).to include(record)
338
+ end
339
+
340
+ it 'shouldn\'t have a problem with non-integer keys' do
341
+ fill_product_data(prod_interface)
342
+ expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
343
+ expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
344
+ end
345
+
346
+ end
347
+ ##
348
+
349
+
350
+ describe '#delete' do
351
+
352
+ def list_contains(ifce, id)
353
+ ifce.list.find {|x| x[ifce.id_fld] == id }
354
+ end
355
+
356
+ let(:id) { interface.list.first[:id] }
357
+
358
+ it 'raises CantContinue if anything hinky happens with the ID' do
359
+ expect{ interface.delete(:foo) }.to raise_exception CantContinue
360
+ expect{ interface.delete(99) }.to raise_exception CantContinue
361
+ end
362
+
363
+ it 'makes the record at ID go away' do
364
+ expect( list_contains(interface, id) ).to be_truthy
365
+ interface.delete(id)
366
+ expect( list_contains(interface, id) ).to be_falsy
367
+ end
368
+
369
+ it 'shouldn\'t have a problem with non-integer keys' do
370
+ fill_product_data(prod_interface)
371
+ expect( list_contains(prod_interface, "foo") ).to be_truthy
372
+ prod_interface.delete("foo")
373
+ expect( list_contains(prod_interface, "foo") ).to be_falsy
374
+ end
375
+
376
+ end
377
+ ##
378
+
379
+
380
+ describe '#execute' do
381
+
382
+ let(:sql) { 'delete from customer where qty < 2.0;' }
383
+
384
+ it 'requires an SQL string' do
385
+ expect{ interface.execute }.to raise_exception ArgumentError
386
+ expect{ interface.execute(nil) }.to raise_exception ArgumentError
387
+ expect{ interface.execute(14) }.to raise_exception ArgumentError
388
+ end
389
+
390
+ it 'raises some sort of Pod4 error if it runs into problems' do
391
+ expect{ interface.execute('delete from not_a_table') }.
392
+ to raise_exception Pod4Error
393
+
394
+ end
395
+
396
+ it 'executes the string' do
397
+ expect{ interface.execute(sql) }.not_to raise_exception
398
+ expect( interface.list.size ).to eq(data.size - 1)
399
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
400
+ end
401
+
402
+ end
403
+ ##
404
+
405
+
406
+ describe '#select' do
407
+
408
+ it 'requires an SQL string' do
409
+ expect{ interface.select }.to raise_exception ArgumentError
410
+ expect{ interface.select(nil) }.to raise_exception ArgumentError
411
+ expect{ interface.select(14) }.to raise_exception ArgumentError
412
+ end
413
+
414
+ it 'raises some sort of Pod4 error if it runs into problems' do
415
+ expect{ interface.select('select * from not_a_table') }.
416
+ to raise_exception Pod4Error
417
+
418
+ end
419
+
420
+ it 'returns the result of the sql' do
421
+ sql1 = 'select name from customer where qty < 2.0;'
422
+ sql2 = 'select name from customer where qty < 0.0;'
423
+
424
+ expect{ interface.select(sql1) }.not_to raise_exception
425
+ expect( interface.select(sql1) ).to eq( [{name: 'Barney'}] )
426
+ expect( interface.select(sql2) ).to eq( [] )
427
+ end
428
+
429
+ it 'works if you pass a non-select' do
430
+ # By which I mean: still executes the SQL; returns []
431
+ sql = 'delete from customer where qty < 2.0;'
432
+ ret = interface.select(sql)
433
+
434
+ expect( interface.list.size ).to eq(data.size - 1)
435
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
436
+ expect( ret ).to eq( [] )
437
+ end
438
+
439
+ end
440
+ ##
441
+
442
+
443
+ describe "#executep" do
444
+ # For the time being lets assume that Sequel does its job and the three modes we are calling
445
+ # actually work
446
+
447
+ let(:sql) { 'delete from customer where qty < ?;' }
448
+
449
+ it 'requires an SQL string and a mode' do
450
+ expect{ interface.executep }.to raise_exception ArgumentError
451
+ expect{ interface.executep(nil) }.to raise_exception ArgumentError
452
+ expect{ interface.executep(14, :update) }.to raise_exception ArgumentError
453
+ expect{ interface.executep(14, :update, 2) }.to raise_exception ArgumentError
454
+ end
455
+
456
+ it 'requires the mode to be valid' do
457
+ expect{ interface.executep(sql, :foo, 2) }.to raise_exception ArgumentError
458
+ end
459
+
460
+ it 'raises some sort of Pod4 error if it runs into problems' do
461
+ expect{ interface.executep('delete from not_a_table where thingy = ?', :delete, 14) }.
462
+ to raise_exception Pod4Error
463
+
464
+ end
465
+
466
+ it 'executes the string' do
467
+ expect{ interface.executep(sql, :delete, 2.0) }.not_to raise_exception
468
+ expect( interface.list.size ).to eq(data.size - 1)
469
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
470
+ end
471
+
472
+ end
473
+
474
+
475
+ describe "#selectp" do
476
+
477
+ it 'requires an SQL string' do
478
+ expect{ interface.selectp }.to raise_exception ArgumentError
479
+ expect{ interface.selectp(nil) }.to raise_exception ArgumentError
480
+ expect{ interface.selectp(14) }.to raise_exception ArgumentError
481
+ end
482
+
483
+ it 'raises some sort of Pod4 error if it runs into problems' do
484
+ expect{ interface.selectp('select * from not_a_table where thingy = ?', 14) }.
485
+ to raise_exception Pod4Error
486
+
487
+ end
488
+
489
+ it 'returns the result of the sql' do
490
+ sql = 'select name from customer where qty < ?;'
491
+
492
+ expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
493
+ expect( interface.selectp(sql, 2.0) ).to eq( [{name: 'Barney'}] )
494
+ expect( interface.selectp(sql, 0.0) ).to eq( [] )
495
+ end
496
+
497
+ it 'works if you pass a non-select' do
498
+ # By which I mean: still executes the SQL; returns []
499
+ sql = 'delete from customer where qty < ?;'
500
+ ret = interface.selectp(sql, 2.0)
501
+
502
+ expect( interface.list.size ).to eq(data.size - 1)
503
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
504
+ expect( ret ).to eq( [] )
505
+ end
506
+
507
+ end
508
+
509
+
510
+ end
511
+