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.
@@ -1,469 +0,0 @@
1
- # This is a copy of spec/mri/pg_interface_spec.rb; you can use it to judge
2
- # whether pg_jruby is currently a good drop-in replacement for pg... :(
3
-
4
- require 'pod4/pg_interface'
5
- require 'pg'
6
-
7
- require_relative '../common/shared_examples_for_interface'
8
- require_relative '../fixtures/database'
9
-
10
-
11
- class TestPgInterface < PgInterface
12
- set_table :customer
13
- set_id_fld :id
14
- end
15
-
16
- class SchemaPgInterface < PgInterface
17
- set_schema :public
18
- set_table :customer
19
- set_id_fld :id
20
- end
21
-
22
- class BadPgInterface1 < PgInterface
23
- set_table :customer
24
- end
25
-
26
- class BadPgInterface2 < PgInterface
27
- set_id_fld :id
28
- end
29
-
30
-
31
- describe TestPgInterface do
32
-
33
- def db_setup(connect)
34
- client = PG.connect(connect)
35
-
36
- client.exec(%Q|drop table if exists customer;|)
37
-
38
- client.exec(%Q|
39
- create table customer (
40
- id serial,
41
- name text,
42
- level real null,
43
- day date null,
44
- timestamp timestamp null,
45
- price money null,
46
- qty numeric null );| )
47
-
48
- ensure
49
- client.finish if client
50
- end
51
-
52
-
53
- def fill_data(ifce)
54
- @data.each{|r| ifce.create(r) }
55
- end
56
-
57
-
58
- before(:all) do
59
- @connect_hash = DB[:pg]
60
- db_setup(@connect_hash)
61
-
62
- @data = []
63
- @data << { name: 'Barney',
64
- level: 1.23,
65
- day: Date.parse("2016-01-01"),
66
- timestamp: Time.parse('2015-01-01 12:11'),
67
- price: BigDecimal.new("1.24"),
68
- qty: BigDecimal.new("1.25") }
69
-
70
- @data << { name: 'Fred',
71
- level: 2.34,
72
- day: Date.parse("2016-02-02"),
73
- timestamp: Time.parse('2015-01-02 12:22'),
74
- price: BigDecimal.new("2.35"),
75
- qty: BigDecimal.new("2.36") }
76
-
77
- @data << { name: 'Betty',
78
- level: 3.45,
79
- day: Date.parse("2016-03-03"),
80
- timestamp: Time.parse('2015-01-03 12:33'),
81
- price: BigDecimal.new("3.46"),
82
- qty: BigDecimal.new("3.47") }
83
-
84
- end
85
-
86
-
87
- before do
88
- # TRUNCATE TABLE also resets the identity counter
89
- interface.execute(%Q|truncate table customer restart identity;|)
90
- end
91
-
92
-
93
- let(:interface) do
94
- TestPgInterface.new(@connect_hash)
95
- end
96
-
97
- #####
98
-
99
-
100
- it_behaves_like 'an interface' do
101
-
102
- let(:interface) do
103
- TestPgInterface.new(@connect_hash)
104
- end
105
-
106
- let(:record) { {name: 'Barney'} }
107
-
108
- end
109
- ##
110
-
111
-
112
- describe 'PgInterface.set_schema' do
113
- it 'takes one argument' do
114
- expect( PgInterface ).to respond_to(:set_schema).with(1).argument
115
- end
116
- end
117
- ##
118
-
119
-
120
- describe 'PgInterface.schema' do
121
- it 'returns the schema' do
122
- expect( SchemaPgInterface.schema ).to eq :public
123
- end
124
-
125
- it 'is optional' do
126
- expect{ TestPgInterface.schema }.not_to raise_exception
127
- expect( TestPgInterface.schema ).to eq nil
128
- end
129
- end
130
- ##
131
-
132
-
133
- describe 'PgInterface.set_table' do
134
- it 'takes one argument' do
135
- expect( PgInterface ).to respond_to(:set_table).with(1).argument
136
- end
137
- end
138
- ##
139
-
140
-
141
- describe 'PgInterface.table' do
142
- it 'returns the table' do
143
- expect( TestPgInterface.table ).to eq :customer
144
- end
145
- end
146
- ##
147
-
148
-
149
- describe 'PgInterface.set_id_fld' do
150
- it 'takes one argument' do
151
- expect( PgInterface ).to respond_to(:set_id_fld).with(1).argument
152
- end
153
- end
154
- ##
155
-
156
-
157
- describe 'PgInterface.id_fld' do
158
- it 'returns the ID field name' do
159
- expect( TestPgInterface.id_fld ).to eq :id
160
- end
161
- end
162
- ##
163
-
164
-
165
- describe '#new' do
166
-
167
- it 'requires a TinyTds connection string' do
168
- expect{ TestPgInterface.new }.to raise_exception ArgumentError
169
- expect{ TestPgInterface.new(nil) }.to raise_exception ArgumentError
170
- expect{ TestPgInterface.new('foo') }.to raise_exception ArgumentError
171
-
172
- expect{ TestPgInterface.new(@connect_hash) }.not_to raise_exception
173
- end
174
-
175
- end
176
- ##
177
-
178
-
179
- describe '#quoted_table' do
180
-
181
- it 'returns just the table when the schema is not set' do
182
- expect( interface.quoted_table ).to eq( %Q|"customer"| )
183
- end
184
-
185
- it 'returns the schema plus table when the schema is set' do
186
- ifce = SchemaPgInterface.new(@connect_hash)
187
- expect( ifce.quoted_table ).to eq( %|"public"."customer"| )
188
- end
189
-
190
- end
191
- ##
192
-
193
-
194
- describe '#create' do
195
-
196
- let(:hash) { {name: 'Bam-Bam', price: 4.44} }
197
- let(:ot) { Octothorpe.new(name: 'Wilma', price: 5.55) }
198
-
199
- it 'raises a Pod4::DatabaseError if anything goes wrong' do
200
- expect{ interface.create(one: 'two') }.to raise_exception DatabaseError
201
- end
202
-
203
- it 'creates the record when given a hash' do
204
- # kinda impossible to seperate these two tests
205
- id = interface.create(hash)
206
-
207
- expect{ interface.read(id) }.not_to raise_exception
208
- expect( interface.read(id).to_h ).to include hash
209
- end
210
-
211
- it 'creates the record when given an Octothorpe' do
212
- id = interface.create(ot)
213
-
214
- expect{ interface.read(id) }.not_to raise_exception
215
- expect( interface.read(id).to_h ).to include ot.to_h
216
- end
217
-
218
- it 'shouldnt have a problem with record values of nil' do
219
- record = {name: 'Ranger', price: nil}
220
- expect{ interface.create(record) }.not_to raise_exception
221
- id = interface.create(record)
222
- expect( interface.read(id).to_h ).to include(record)
223
- end
224
-
225
- it 'shouldnt have a problem with strings containing special characters' do
226
- record = {name: %Q|T'Challa""|, price: nil}
227
- expect{ interface.create(record) }.not_to raise_exception
228
- id = interface.create(record)
229
- expect( interface.read(id).to_h ).to include(record)
230
- end
231
-
232
- end
233
- ##
234
-
235
-
236
- describe '#read' do
237
- before { fill_data(interface) }
238
-
239
- it 'returns the record for the id as an Octothorpe' do
240
- rec = interface.read(2)
241
- expect( rec ).to be_a_kind_of Octothorpe
242
- expect( rec.>>.name ).to eq 'Fred'
243
- end
244
-
245
- it 'raises a Pod4::CantContinue if the ID is bad' do
246
- expect{ interface.read(:foo) }.to raise_exception CantContinue
247
- end
248
-
249
- it 'returns an empty Octothorpe if no record matches the ID' do
250
- expect{ interface.read(99) }.not_to raise_exception
251
- expect( interface.read(99) ).to be_a_kind_of Octothorpe
252
- expect( interface.read(99) ).to be_empty
253
- end
254
-
255
- it 'returns real fields as Float' do
256
- level = interface.read(1).>>.level
257
-
258
- expect( level ).to be_a_kind_of Float
259
- expect( level ).to be_within(0.001).of( @data.first[:level] )
260
- end
261
-
262
- it 'returns date fields as Date' do
263
- date = interface.read(1).>>.day
264
-
265
- expect( date ).to be_a_kind_of Date
266
- expect( date ).to eq @data.first[:day]
267
- end
268
-
269
- it 'returns datetime fields as Time' do
270
- timestamp = interface.read(1).>>.timestamp
271
-
272
- expect( timestamp ).to be_a_kind_of Time
273
- expect( timestamp ).to eq @data.first[:timestamp]
274
- end
275
-
276
- it 'returns numeric fields as BigDecimal' do
277
- qty = interface.read(1).>>.qty
278
-
279
- expect( qty ).to be_a_kind_of BigDecimal
280
- expect( qty ).to eq @data.first[:qty]
281
- end
282
-
283
- it 'returns money fields as BigDecimal' do
284
- price = interface.read(1).>>.price
285
-
286
- expect( price ).to be_a_kind_of BigDecimal
287
- expect( price ).to eq @data.first[:price]
288
- end
289
-
290
- end
291
- ##
292
-
293
-
294
- describe '#list' do
295
- before { fill_data(interface) }
296
-
297
- it 'has an optional selection parameter, a hash' do
298
- # Actually it does not have to be a hash, but FTTB we only support that.
299
- expect{ interface.list(name: 'Barney') }.not_to raise_exception
300
- end
301
-
302
- it 'returns an array of Octothorpes that match the records' do
303
- # convert each OT to a hash and remove the ID key
304
- arr = interface.list.map {|ot| x = ot.to_h; x.delete(:id); x }
305
-
306
- expect( arr ).to match_array @data
307
- end
308
-
309
- it 'returns a subset of records based on the selection parameter' do
310
- expect( interface.list(name: 'Fred').size ).to eq 1
311
-
312
- expect( interface.list(name: 'Betty').first.to_h ).
313
- to include(name: 'Betty')
314
-
315
- end
316
-
317
- it 'returns an empty Array if nothing matches' do
318
- expect( interface.list(name: 'Yogi') ).to eq([])
319
- end
320
-
321
- it 'raises ArgumentError if the selection criteria is nonsensical' do
322
- expect{ interface.list('foo') }.to raise_exception ArgumentError
323
- end
324
-
325
- end
326
- ##
327
-
328
-
329
- describe '#update' do
330
- before { fill_data(interface) }
331
-
332
- let(:id) { interface.list.first[:id] }
333
-
334
- def float_price(row)
335
- row[:price] = row[:price].to_f
336
- row
337
- end
338
-
339
- it 'updates the record at ID with record parameter' do
340
- record = {name: 'Booboo', price: 99.99}
341
- interface.update(id, record)
342
-
343
- # It so happens that TinyTds returns money as BigDecimal --
344
- # this is a really good thing, even though it screws with our test.
345
- expect( float_price( interface.read(id).to_h ) ).to include(record)
346
- end
347
-
348
- it 'raises a CantContinue if anything weird happens with the ID' do
349
- expect{ interface.update(99, name: 'Booboo') }.
350
- to raise_exception CantContinue
351
-
352
- end
353
-
354
- it 'raises a DatabaseError if anything weird happens with the record' do
355
- expect{ interface.update(id, smarts: 'more') }.
356
- to raise_exception DatabaseError
357
-
358
- end
359
-
360
- it 'shouldnt have a problem with record values of nil' do
361
- record = {name: 'Ranger', price: nil}
362
- expect{ interface.update(id, record) }.not_to raise_exception
363
- expect( interface.read(id).to_h ).to include(record)
364
- end
365
-
366
- it 'shouldnt have a problem with strings containing special characters' do
367
- record = {name: %Q|T'Challa""|, price: nil}
368
- expect{ interface.update(id, record) }.not_to raise_exception
369
- expect( interface.read(id).to_h ).to include(record)
370
- end
371
-
372
- end
373
- ##
374
-
375
-
376
- describe '#delete' do
377
-
378
- def list_contains(id)
379
- interface.list.find {|x| x[interface.id_fld] == id }
380
- end
381
-
382
- let(:id) { interface.list.first[:id] }
383
-
384
- before { fill_data(interface) }
385
-
386
- it 'raises CantContinue if anything hinky happens with the id' do
387
- expect{ interface.delete(:foo) }.to raise_exception CantContinue
388
- expect{ interface.delete(99) }.to raise_exception CantContinue
389
- end
390
-
391
- it 'makes the record at ID go away' do
392
- expect( list_contains(id) ).to be_truthy
393
- interface.delete(id)
394
- expect( list_contains(id) ).to be_falsy
395
- end
396
-
397
- end
398
- ##
399
-
400
-
401
- describe '#execute' do
402
-
403
- let(:sql) { 'delete from customer where cast(price as numeric) < 2.0;' }
404
-
405
- before { fill_data(interface) }
406
-
407
- it 'requires an SQL string' do
408
- expect{ interface.execute }.to raise_exception ArgumentError
409
- expect{ interface.execute(nil) }.to raise_exception ArgumentError
410
- expect{ interface.execute(14) }.to raise_exception ArgumentError
411
- end
412
-
413
- it 'raises some sort of Pod4 error if it runs into problems' do
414
- expect{ interface.execute('delete from not_a_table') }.
415
- to raise_exception Pod4Error
416
-
417
- end
418
-
419
- it 'executes the string' do
420
- expect{ interface.execute(sql) }.not_to raise_exception
421
- expect( interface.list.size ).to eq(@data.size - 1)
422
- expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
423
- end
424
-
425
- end
426
- ##
427
-
428
-
429
- describe '#select' do
430
-
431
- before { fill_data(interface) }
432
-
433
- it 'requires an SQL string' do
434
- expect{ interface.select }.to raise_exception ArgumentError
435
- expect{ interface.select(nil) }.to raise_exception ArgumentError
436
- expect{ interface.select(14) }.to raise_exception ArgumentError
437
- end
438
-
439
- it 'raises some sort of Pod4 error if it runs into problems' do
440
- expect{ interface.select('select * from not_a_table') }.
441
- to raise_exception Pod4Error
442
-
443
- end
444
-
445
- it 'returns the result of the sql' do
446
- sql1 = 'select name from customer where cast(price as numeric) < 2.0;'
447
- sql2 = 'select name from customer where cast(price as numeric) < 0.0;'
448
-
449
- expect{ interface.select(sql1) }.not_to raise_exception
450
- expect( interface.select(sql1) ).to eq( [{name: 'Barney'}] )
451
- expect( interface.select(sql2) ).to eq( [] )
452
- end
453
-
454
- it 'works if you pass a non-select' do
455
- # By which I mean: still executes the SQL; returns []
456
- sql = 'delete from customer where cast(price as numeric) < 2.0;'
457
- ret = interface.select(sql)
458
-
459
- expect( interface.list.size ).to eq(@data.size - 1)
460
- expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
461
- expect( ret ).to eq( [] )
462
- end
463
-
464
- end
465
- ##
466
-
467
-
468
- end
469
-