pod4 0.7.2 → 0.8.0

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.
@@ -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