pod4 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,9 @@ require_relative '../fixtures/database'
8
8
  class TestPgInterface < PgInterface
9
9
  set_table :customer
10
10
  set_id_fld :id
11
+
12
+ # We open a lot of connections, unusually
13
+ def stop; close; end
11
14
  end
12
15
 
13
16
  class SchemaPgInterface < PgInterface
@@ -24,23 +27,33 @@ class BadPgInterface2 < PgInterface
24
27
  set_id_fld :id
25
28
  end
26
29
 
30
+ class ProdPgInterface < PgInterface
31
+ set_table :product
32
+ set_id_fld :code
33
+ end
34
+
27
35
 
28
36
  describe TestPgInterface do
29
37
 
30
38
  def db_setup(connect)
31
39
  client = PG.connect(connect)
32
40
 
33
- client.exec(%Q|drop table if exists customer;|)
34
-
35
41
  client.exec(%Q|
42
+ drop table if exists customer;
43
+ drop table if exists product;
44
+
36
45
  create table customer (
37
- id serial,
46
+ id serial primary key,
38
47
  name text,
39
48
  level real null,
40
49
  day date null,
41
50
  timestamp timestamp null,
42
51
  price money null,
43
- qty numeric null );| )
52
+ qty numeric null );
53
+
54
+ create table product (
55
+ code text,
56
+ name text );| )
44
57
 
45
58
  ensure
46
59
  client.finish if client
@@ -52,6 +65,11 @@ describe TestPgInterface do
52
65
  end
53
66
 
54
67
 
68
+ def fill_product_data(ifce)
69
+ ifce.create( {code: "foo", name: "bar"} )
70
+ end
71
+
72
+
55
73
  before(:all) do
56
74
  @connect_hash = DB[:pg]
57
75
  db_setup(@connect_hash)
@@ -82,8 +100,16 @@ describe TestPgInterface do
82
100
 
83
101
 
84
102
  before do
85
- # TRUNCATE TABLE also resets the identity counter
86
- interface.execute(%Q|truncate table customer restart identity;|)
103
+ interface.execute(%Q|
104
+ truncate table customer restart identity;
105
+ truncate table product;|)
106
+
107
+ end
108
+
109
+
110
+ after do
111
+ # We open a lot of connections, unusually
112
+ interface.stop if interface
87
113
  end
88
114
 
89
115
 
@@ -91,6 +117,10 @@ describe TestPgInterface do
91
117
  TestPgInterface.new(@connect_hash)
92
118
  end
93
119
 
120
+ let(:prod_interface) do
121
+ ProdPgInterface.new(@connect_hash)
122
+ end
123
+
94
124
  #####
95
125
 
96
126
 
@@ -212,20 +242,29 @@ describe TestPgInterface do
212
242
  expect( interface.read(id).to_h ).to include ot.to_h
213
243
  end
214
244
 
215
- it 'shouldnt have a problem with record values of nil' do
245
+ it 'shouldn\'t have a problem with record values of nil' do
216
246
  record = {name: 'Ranger', price: nil}
217
247
  expect{ interface.create(record) }.not_to raise_exception
218
248
  id = interface.create(record)
219
249
  expect( interface.read(id).to_h ).to include(record)
220
250
  end
221
251
 
222
- it 'shouldnt have a problem with strings containing special characters' do
252
+ it 'shouldn\'t have a problem with strings containing special characters' do
223
253
  record = {name: %Q|T'Challa""|, price: nil}
224
254
  expect{ interface.create(record) }.not_to raise_exception
225
255
  id = interface.create(record)
226
256
  expect( interface.read(id).to_h ).to include(record)
227
257
  end
228
258
 
259
+ it 'shouldn\'t have a problem with non-integer keys' do
260
+ hash = {code: "foo", name: "bar"}
261
+ id = prod_interface.create( Octothorpe.new(hash) )
262
+
263
+ expect( id ).to eq "foo"
264
+ expect{ prod_interface.read("foo") }.not_to raise_exception
265
+ expect( prod_interface.read("foo").to_h ).to include hash
266
+ end
267
+
229
268
  end
230
269
  ##
231
270
 
@@ -284,6 +323,14 @@ describe TestPgInterface do
284
323
  expect( price ).to eq @data.first[:price]
285
324
  end
286
325
 
326
+ it 'shouldn\'t have a problem with non-integer keys' do
327
+ # this is a 100% overlap with the create test above...
328
+ fill_product_data(prod_interface)
329
+
330
+ expect{ prod_interface.read("foo") }.not_to raise_exception
331
+ expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
332
+ end
333
+
287
334
  end
288
335
  ##
289
336
 
@@ -337,8 +384,6 @@ describe TestPgInterface do
337
384
  record = {name: 'Booboo', price: 99.99}
338
385
  interface.update(id, record)
339
386
 
340
- # It so happens that TinyTds returns money as BigDecimal --
341
- # this is a really good thing, even though it screws with our test.
342
387
  expect( float_price( interface.read(id).to_h ) ).to include(record)
343
388
  end
344
389
 
@@ -354,26 +399,32 @@ describe TestPgInterface do
354
399
 
355
400
  end
356
401
 
357
- it 'shouldnt have a problem with record values of nil' do
402
+ it 'shouldn\'t have a problem with record values of nil' do
358
403
  record = {name: 'Ranger', price: nil}
359
404
  expect{ interface.update(id, record) }.not_to raise_exception
360
405
  expect( interface.read(id).to_h ).to include(record)
361
406
  end
362
407
 
363
- it 'shouldnt have a problem with strings containing special characters' do
408
+ it 'shouldn\'t have a problem with strings containing special characters' do
364
409
  record = {name: %Q|T'Challa""|, price: nil}
365
410
  expect{ interface.update(id, record) }.not_to raise_exception
366
411
  expect( interface.read(id).to_h ).to include(record)
367
412
  end
368
413
 
414
+ it 'shouldn\'t have a problem with non-integer keys' do
415
+ fill_product_data(prod_interface)
416
+ expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
417
+ expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
418
+ end
419
+
369
420
  end
370
421
  ##
371
422
 
372
423
 
373
424
  describe '#delete' do
374
425
 
375
- def list_contains(id)
376
- interface.list.find {|x| x[interface.id_fld] == id }
426
+ def list_contains(ifce, id)
427
+ ifce.list.find {|x| x[ifce.id_fld] == id }
377
428
  end
378
429
 
379
430
  let(:id) { interface.list.first[:id] }
@@ -386,9 +437,16 @@ describe TestPgInterface do
386
437
  end
387
438
 
388
439
  it 'makes the record at ID go away' do
389
- expect( list_contains(id) ).to be_truthy
440
+ expect( list_contains(interface, id) ).to be_truthy
390
441
  interface.delete(id)
391
- expect( list_contains(id) ).to be_falsy
442
+ expect( list_contains(interface, id) ).to be_falsy
443
+ end
444
+
445
+ it 'shouldn\'t have a problem with non-integer keys' do
446
+ fill_product_data(prod_interface)
447
+ expect( list_contains(prod_interface, "foo") ).to be_truthy
448
+ prod_interface.delete("foo")
449
+ expect( list_contains(prod_interface, "foo") ).to be_falsy
392
450
  end
393
451
 
394
452
  end
@@ -423,6 +481,34 @@ describe TestPgInterface do
423
481
  ##
424
482
 
425
483
 
484
+ describe '#executep' do
485
+
486
+ let(:sql) { 'delete from customer where cast(price as numeric) < %s and name = %s;' }
487
+
488
+ before { fill_data(interface) }
489
+
490
+ it 'requires an SQL string' do
491
+ expect{ interface.executep }.to raise_exception ArgumentError
492
+ expect{ interface.executep(nil) }.to raise_exception ArgumentError
493
+ expect{ interface.executep(14) }.to raise_exception ArgumentError
494
+ end
495
+
496
+ it 'raises some sort of Pod4 error if it runs into problems' do
497
+ expect{ interface.executep('delete from not_a_table where foo = %s', 12) }.
498
+ to raise_exception Pod4Error
499
+
500
+ end
501
+
502
+ it 'executes the string with the given parameters' do
503
+ expect{ interface.executep(sql, 12.0, 'Barney') }.not_to raise_exception
504
+ expect( interface.list.size ).to eq(@data.size - 1)
505
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
506
+ end
507
+
508
+ end
509
+ ##
510
+
511
+
426
512
  describe '#select' do
427
513
 
428
514
  before { fill_data(interface) }
@@ -462,5 +548,43 @@ describe TestPgInterface do
462
548
  ##
463
549
 
464
550
 
551
+ describe '#selectp' do
552
+
553
+ before { fill_data(interface) }
554
+
555
+ it 'requires an SQL string' do
556
+ expect{ interface.selectp }.to raise_exception ArgumentError
557
+ expect{ interface.selectp(nil) }.to raise_exception ArgumentError
558
+ expect{ interface.selectp(14) }.to raise_exception ArgumentError
559
+ end
560
+
561
+ it 'raises some sort of Pod4 error if it runs into problems' do
562
+ expect{ interface.selectp('select * from not_a_table where thingy = %s', 12) }.
563
+ to raise_exception Pod4Error
564
+
565
+ end
566
+
567
+ it 'returns the result of the sql' do
568
+ sql = 'select name from customer where cast(price as numeric) < %s;'
569
+
570
+ expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
571
+ expect( interface.selectp(sql, 2.0) ).to eq( [{name: 'Barney'}] )
572
+ expect( interface.selectp(sql, 0.0) ).to eq( [] )
573
+ end
574
+
575
+ it 'works if you pass a non-select' do
576
+ sql = 'delete from customer where cast(price as numeric) < %s;'
577
+ ret = interface.selectp(sql, 2.0)
578
+
579
+ expect( interface.list.size ).to eq(@data.size - 1)
580
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
581
+ expect( ret ).to eq( [] )
582
+ end
583
+
584
+
585
+ end
586
+ ##
587
+
588
+
465
589
  end
466
590
 
@@ -27,6 +27,13 @@ class BadSequelInterface2 < SequelInterface
27
27
  set_id_fld :id
28
28
  end
29
29
 
30
+ class ProdSequelInterface < SequelInterface
31
+ set_table :product
32
+ set_id_fld :code
33
+ end
34
+
35
+
36
+
30
37
 
31
38
  describe TestSequelInterface do
32
39
 
@@ -63,14 +70,18 @@ describe TestSequelInterface do
63
70
  data.each{|r| ifce.create(r) }
64
71
  end
65
72
 
73
+ def fill_product_data(ifce)
74
+ ifce.create( {code: "foo", name: "bar"} )
75
+ end
76
+
77
+
66
78
  # This is stolen almost verbatim from the Sequel Readme. We use an in-memory
67
79
  # sqlite database, and we assume that Sequel is sane and behaves broadly the
68
80
  # same for our limited purposes as it would when talking to TinyTDS or Pg.
69
- # This may be an entirely unwarranted assumption. If so, we will have to
70
- # change this. But in any case, we are not in the business of testing Sequel:
71
- # just our interface to it.
81
+ # We test these elsewhere...
72
82
  let (:db) do
73
83
  db = Sequel.sqlite
84
+
74
85
  db.create_table :customer do
75
86
  primary_key :id
76
87
  String :name
@@ -79,10 +90,17 @@ describe TestSequelInterface do
79
90
  Time :timestamp
80
91
  BigDecimal :price, :size=>[10.2] # Sequel doesn't support money
81
92
  end
93
+
94
+ db.create_table :product do
95
+ String :code, :primary_key => true
96
+ String :name
97
+ end
98
+
82
99
  db
83
100
  end
84
101
 
85
- let(:interface) { TestSequelInterface.new(db) }
102
+ let(:interface) { TestSequelInterface.new(db) }
103
+ let(:prod_interface) { ProdSequelInterface.new(db) }
86
104
 
87
105
  before do
88
106
  fill_data(interface)
@@ -108,8 +126,6 @@ describe TestSequelInterface do
108
126
  end
109
127
 
110
128
  let(:record) { {name: 'Barney', price: 1.11} }
111
- #let(:record_id) { 'Barney' }
112
-
113
129
  end
114
130
  ##
115
131
 
@@ -215,6 +231,7 @@ describe TestSequelInterface do
215
231
  # kinda impossible to seperate these two tests
216
232
  id = interface.create(hash)
217
233
 
234
+ expect( id ).not_to be_nil
218
235
  expect{ interface.read(id) }.not_to raise_exception
219
236
  expect( interface.read(id).to_h ).to include hash
220
237
  end
@@ -222,6 +239,7 @@ describe TestSequelInterface do
222
239
  it 'creates the record when given an Octothorpe' do
223
240
  id = interface.create(ot)
224
241
 
242
+ expect( id ).not_to be_nil
225
243
  expect{ interface.read(id) }.not_to raise_exception
226
244
  expect( interface.read(id).to_h ).to include ot.to_h
227
245
  end
@@ -231,20 +249,29 @@ describe TestSequelInterface do
231
249
  expect{ interface.create(name: :Booboo) }.not_to raise_exception
232
250
  end
233
251
 
234
- it 'shouldnt have a problem with record values of nil' do
252
+ it 'shouldn\'t have a problem with record values of nil' do
235
253
  record = {name: 'Ranger', price: nil}
236
254
  expect{ interface.create(record) }.not_to raise_exception
237
255
  id = interface.create(record)
238
256
  expect( interface.read(id).to_h ).to include(record)
239
257
  end
240
258
 
241
- it 'shouldnt have a problem with strings containing special characters' do
259
+ it 'shouldn\'t have a problem with strings containing special characters' do
242
260
  record = {name: "T'Challa[]", price: nil}
243
261
  expect{ interface.create(record) }.not_to raise_exception
244
262
  id = interface.create(record)
245
263
  expect( interface.read(id).to_h ).to include(record)
246
264
  end
247
265
 
266
+ it 'shouldn\'t have a problem with non-integer keys' do
267
+ hash = {code: "foo", name: "bar"}
268
+ id = prod_interface.create( Octothorpe.new(hash) )
269
+
270
+ expect( id ).to eq "foo"
271
+ expect{ prod_interface.read("foo") }.not_to raise_exception
272
+ expect( prod_interface.read("foo").to_h ).to include hash
273
+ end
274
+
248
275
  end
249
276
  ##
250
277
 
@@ -293,6 +320,14 @@ describe TestSequelInterface do
293
320
  expect( price ).to eq data.first[:price]
294
321
  end
295
322
 
323
+ it 'shouldn\'t have a problem with non-integer keys' do
324
+ # this is a 100% overlap with the create test above...
325
+ fill_product_data(prod_interface)
326
+
327
+ expect{ prod_interface.read("foo") }.not_to raise_exception
328
+ expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
329
+ end
330
+
296
331
  end
297
332
  ##
298
333
 
@@ -305,12 +340,31 @@ describe TestSequelInterface do
305
340
  expect{ interface.list(name: 'Barney') }.not_to raise_exception
306
341
  end
307
342
 
343
+ =begin
308
344
  it 'returns an array of Octothorpes that match the records' do
309
345
  # convert each OT to a hash and remove the ID key
310
346
  arr = interface.list.map {|ot| x = ot.to_h; x.delete(:id); x }
311
347
 
312
348
  expect( arr ).to match_array data
313
349
  end
350
+ =end
351
+
352
+ it 'returns an array of Octothorpes that match the records' do
353
+ arr = interface.list.map {|ot| x = ot.to_h}
354
+
355
+ expect( arr.size ).to eq(data.size)
356
+
357
+ data.each do |d|
358
+ r = arr.find{|x| x[:name] == d[:name] }
359
+ expect( r ).not_to be_nil
360
+ expect( r[:level] ).to be_within(0.001).of( d[:level] )
361
+ expect( r[:day] ).to eq d[:day]
362
+ expect( r[:timestamp] ).to eq d[:timestamp]
363
+ expect( r[:qty] ).to eq d[:qty]
364
+ end
365
+
366
+ end
367
+
314
368
 
315
369
  it 'returns a subset of records based on the selection parameter' do
316
370
  expect( interface.list(name: 'Fred').size ).to eq 1
@@ -385,14 +439,20 @@ describe TestSequelInterface do
385
439
  expect( interface.read(id).to_h ).to include(record)
386
440
  end
387
441
 
442
+ it 'shouldn\'t have a problem with non-integer keys' do
443
+ fill_product_data(prod_interface)
444
+ expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
445
+ expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
446
+ end
447
+
388
448
  end
389
449
  ##
390
450
 
391
451
 
392
452
  describe '#delete' do
393
453
 
394
- def list_contains(id)
395
- interface.list.find {|x| x[interface.id_fld] == id }
454
+ def list_contains(ifce, id)
455
+ ifce.list.find {|x| x[ifce.id_fld] == id }
396
456
  end
397
457
 
398
458
  let(:id) { interface.list.first[:id] }
@@ -403,9 +463,16 @@ describe TestSequelInterface do
403
463
  end
404
464
 
405
465
  it 'makes the record at ID go away' do
406
- expect( list_contains(id) ).to be_truthy
466
+ expect( list_contains(interface, id) ).to be_truthy
407
467
  interface.delete(id)
408
- expect( list_contains(id) ).to be_falsy
468
+ expect( list_contains(interface, id) ).to be_falsy
469
+ end
470
+
471
+ it 'shouldn\'t have a problem with non-integer keys' do
472
+ fill_product_data(prod_interface)
473
+ expect( list_contains(prod_interface, "foo") ).to be_truthy
474
+ prod_interface.delete("foo")
475
+ expect( list_contains(prod_interface, "foo") ).to be_falsy
409
476
  end
410
477
 
411
478
  end
@@ -471,10 +538,76 @@ describe TestSequelInterface do
471
538
  expect( ret ).to eq( [] )
472
539
  end
473
540
 
474
-
475
541
  end
476
542
  ##
477
543
 
478
544
 
545
+ describe "#executep" do
546
+ # For the time being lets assume that Sequel does its job and the three modes we are calling
547
+ # actually work
548
+
549
+ let(:sql) { 'delete from customer where price < ?;' }
550
+
551
+ it 'requires an SQL string and a mode' do
552
+ expect{ interface.executep }.to raise_exception ArgumentError
553
+ expect{ interface.executep(nil) }.to raise_exception ArgumentError
554
+ expect{ interface.executep(14, :update) }.to raise_exception ArgumentError
555
+ expect{ interface.executep(14, :update, 2) }.to raise_exception ArgumentError
556
+ end
557
+
558
+ it 'requires the mode to be valid' do
559
+ expect{ interface.executep(sql, :foo, 2) }.to raise_exception ArgumentError
560
+ end
561
+
562
+ it 'raises some sort of Pod4 error if it runs into problems' do
563
+ expect{ interface.executep('delete from not_a_table where thingy = ?', :delete, 14) }.
564
+ to raise_exception Pod4Error
565
+
566
+ end
567
+
568
+ it 'executes the string' do
569
+ expect{ interface.executep(sql, :delete, 2.0) }.not_to raise_exception
570
+ expect( interface.list.size ).to eq(data.size - 1)
571
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
572
+ end
573
+
574
+ end
575
+
576
+
577
+ describe "#selectp" do
578
+
579
+ it 'requires an SQL string' do
580
+ expect{ interface.selectp }.to raise_exception ArgumentError
581
+ expect{ interface.selectp(nil) }.to raise_exception ArgumentError
582
+ expect{ interface.selectp(14) }.to raise_exception ArgumentError
583
+ end
584
+
585
+ it 'raises some sort of Pod4 error if it runs into problems' do
586
+ expect{ interface.selectp('select * from not_a_table where thingy = ?', 14) }.
587
+ to raise_exception Pod4Error
588
+
589
+ end
590
+
591
+ it 'returns the result of the sql' do
592
+ sql = 'select name from customer where price < ?;'
593
+
594
+ expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
595
+ expect( interface.selectp(sql, 2.0) ).to eq( [{name: 'Barney'}] )
596
+ expect( interface.selectp(sql, 0.0) ).to eq( [] )
597
+ end
598
+
599
+ it 'works if you pass a non-select' do
600
+ # By which I mean: still executes the SQL; returns []
601
+ sql = 'delete from customer where price < ?;'
602
+ ret = interface.selectp(sql, 2.0)
603
+
604
+ expect( interface.list.size ).to eq(data.size - 1)
605
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
606
+ expect( ret ).to eq( [] )
607
+ end
608
+
609
+ end
610
+
611
+
479
612
  end
480
613