pod4 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-