crud-service 0.0.9 → 0.1.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.
- checksums.yaml +6 -14
- data/lib/crud-service.rb +6 -3
- data/lib/crud-service/api.rb +152 -0
- data/lib/{dal.rb → crud-service/dal.rb} +25 -5
- data/lib/{service.rb → crud-service/service.rb} +7 -1
- data/lib/crud-service/version.rb +5 -0
- data/spec/api_spec.rb +609 -0
- data/spec/dal_spec.rb +1363 -0
- data/spec/{unit/service_spec.rb → service_spec.rb} +23 -23
- data/spec/spec_helper.rb +58 -0
- metadata +70 -106
- data/lib/api.rb +0 -129
- data/spec/helper.rb +0 -12
- data/spec/helpers_spec.rb +0 -30
- data/spec/unit/dal_spec.rb +0 -1174
data/spec/dal_spec.rb
ADDED
@@ -0,0 +1,1363 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CrudService::Dal do
|
4
|
+
before(:each) do
|
5
|
+
@mock_mysql = mysql_mock
|
6
|
+
@mock_memcache = double('Memcache')
|
7
|
+
@mock_log = double('Log')
|
8
|
+
|
9
|
+
@generic_dal = CrudService::Dal.new(@mock_mysql, @mock_memcache, @mock_log)
|
10
|
+
@generic_dal.table_name = "testtable"
|
11
|
+
@generic_dal.cache_prefix = "prefix"
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#initialize' do
|
15
|
+
it 'should inject dependencies correctly' do
|
16
|
+
expect(@generic_dal.mysql).to eq(@mock_mysql)
|
17
|
+
expect(@generic_dal.memcache).to eq(@mock_memcache)
|
18
|
+
expect(@generic_dal.log).to eq(@mock_log)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#cached_query' do
|
23
|
+
it 'should attempt to query the cache before the database' do
|
24
|
+
|
25
|
+
testdata = [ { "field_one" => "one" } ]
|
26
|
+
|
27
|
+
mock_result = mysql_result_mock(testdata)
|
28
|
+
|
29
|
+
query = 'test invalid query'
|
30
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-1")
|
31
|
+
|
32
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
33
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
34
|
+
expect(@mock_mysql).to receive(:query).with(query).and_return(mock_result)
|
35
|
+
expect(@mock_memcache).to receive(:set).ordered.with(query_hash, testdata)
|
36
|
+
|
37
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should not attempt to query the database on a cache hit' do
|
41
|
+
testdata = [ { "field_one" => "one" } ]
|
42
|
+
query = 'test invalid query'
|
43
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-1")
|
44
|
+
|
45
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
46
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(testdata)
|
47
|
+
expect(@mock_mysql).not_to receive(:query)
|
48
|
+
expect(@mock_memcache).not_to receive(:set).ordered
|
49
|
+
|
50
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should handle zero record return' do
|
54
|
+
memcache_null(@mock_memcache)
|
55
|
+
|
56
|
+
query = 'test invalid query'
|
57
|
+
|
58
|
+
expect(@mock_mysql).to receive(:query).with(query).and_return(mysql_result_mock([]))
|
59
|
+
|
60
|
+
expect(@generic_dal.cached_query(query,[])).to eq([])
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should write a new table version to cache when not found' do
|
64
|
+
testdata = [ { "field_one" => "one" } ]
|
65
|
+
|
66
|
+
mock_result = mysql_result_mock(testdata)
|
67
|
+
|
68
|
+
query = 'test invalid query'
|
69
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-1")
|
70
|
+
|
71
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(nil)
|
72
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(nil)
|
73
|
+
expect(@mock_memcache).to receive(:set).ordered.with("prefix-testtable-version",1,nil,{:raw=>true})
|
74
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
75
|
+
expect(@mock_mysql).to receive(:query).ordered.with(query).and_return(mock_result)
|
76
|
+
expect(@mock_memcache).to receive(:set).ordered.with(query_hash, testdata)
|
77
|
+
|
78
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should miss the cache when a table version has changed' do
|
82
|
+
testdata = [ { "field_one" => "one" } ]
|
83
|
+
|
84
|
+
mock_result = mysql_result_mock(testdata)
|
85
|
+
|
86
|
+
query = 'test invalid query'
|
87
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-1")
|
88
|
+
|
89
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
90
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
91
|
+
expect(@mock_mysql).to receive(:query).with(query).and_return(mock_result)
|
92
|
+
expect(@mock_memcache).to receive(:set).ordered.with(query_hash, testdata)
|
93
|
+
|
94
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
95
|
+
|
96
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-2")
|
97
|
+
|
98
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(2)
|
99
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
100
|
+
expect(@mock_mysql).to receive(:query).with(query).and_return(mock_result)
|
101
|
+
expect(@mock_memcache).to receive(:set).ordered.with(query_hash, testdata)
|
102
|
+
|
103
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should lok error and return [] if the database query fails' do
|
107
|
+
testdata = [ { "field_one" => "one" } ]
|
108
|
+
|
109
|
+
mock_result = mysql_result_mock(testdata)
|
110
|
+
|
111
|
+
query = 'test invalid query'
|
112
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-1")
|
113
|
+
|
114
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
115
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
116
|
+
expect(@mock_mysql).to receive(:query).with(query).and_return(mock_result)
|
117
|
+
expect(@mock_memcache).to receive(:set).ordered.with(query_hash, testdata)
|
118
|
+
|
119
|
+
expect(@generic_dal.cached_query(query,[])).to eq(testdata)
|
120
|
+
|
121
|
+
query_hash = "prefix-"+Digest::MD5.hexdigest(query+":testtable-2")
|
122
|
+
|
123
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(2)
|
124
|
+
expect(@mock_memcache).to receive(:get).ordered.with(query_hash).and_return(nil)
|
125
|
+
expect(@mock_mysql).to receive(:query) { raise "TestException"}
|
126
|
+
expect(@mock_log).to receive(:error).with("TestException")
|
127
|
+
|
128
|
+
expect(@generic_dal.cached_query(query,[])).to eq([])
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#build_where' do
|
134
|
+
it 'should return an empty string when called with no query' do
|
135
|
+
query = { }
|
136
|
+
expect(@generic_dal.build_where(query)).to eq ""
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should return a valid where clause when called with a single field query string value' do
|
140
|
+
query = { "one" => "two" }
|
141
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 'two')"
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should return a valid where clause when called with a single field query integer value' do
|
145
|
+
query = { "one" => 2 }
|
146
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 2)"
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should return a valid where clause when called with a single field query float value' do
|
150
|
+
query = { "one" => 2.123 }
|
151
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 2.123)"
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should return a valid where clause when called with a multiple field query' do
|
155
|
+
query = { "one" => "two", "three" => "four" }
|
156
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 'two') AND (`three` = 'four')"
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should return a valid where clause when called with a query with a nil value' do
|
160
|
+
query = { "one" => "two", "three" => nil}
|
161
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 'two') AND (`three` IS NULL)"
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should escape field names' do
|
165
|
+
query = { "on`=1; DROP TABLE countries" => "two" }
|
166
|
+
expect(@generic_dal.build_where(query)).to eq "(`on=1; DROP TABLE countries` = 'two')"
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should escape field values when string based' do
|
170
|
+
query = { "one" => "two'; DROP TABLE countries;" }
|
171
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 'two\\'; DROP TABLE countries;')"
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should not build include or exclude into queries' do
|
175
|
+
query = { "one" => 2, "include" => "subdivisions", "exclude" => "countries", "two"=>3 }
|
176
|
+
expect(@generic_dal.build_where(query)).to eq "(`one` = 2) AND (`two` = 3)"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#build_where_ns_ns' do
|
181
|
+
it 'should return an empty string when called with no query' do
|
182
|
+
query = { }
|
183
|
+
expect(@generic_dal.build_where_ns(query,'a')).to eq ""
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should return a valid where clause when called with a single field query string value' do
|
187
|
+
query = { "one" => "two" }
|
188
|
+
expect(@generic_dal.build_where_ns(query,'b')).to eq "(`b`.`one` = 'two')"
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should return a valid where clause when called with a single field query integer value' do
|
192
|
+
query = { "one" => 2 }
|
193
|
+
expect(@generic_dal.build_where_ns(query,'c')).to eq "(`c`.`one` = 2)"
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should return a valid where clause when called with a single field query float value' do
|
197
|
+
query = { "one" => 2.123 }
|
198
|
+
expect(@generic_dal.build_where_ns(query,'d')).to eq "(`d`.`one` = 2.123)"
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should return a valid where clause when called with a multiple field query' do
|
202
|
+
query = { "one" => "two", "three" => "four" }
|
203
|
+
expect(@generic_dal.build_where_ns(query,'e')).to eq "(`e`.`one` = 'two') AND (`e`.`three` = 'four')"
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should return a valid where clause when called with a query with a nil value' do
|
207
|
+
query = { "one" => "two", "three" => nil}
|
208
|
+
expect(@generic_dal.build_where_ns(query,'f')).to eq "(`f`.`one` = 'two') AND (`f`.`three` IS NULL)"
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should escape field names' do
|
212
|
+
query = { "on`=1; DROP TABLE countries" => "two" }
|
213
|
+
expect(@generic_dal.build_where_ns(query,'g')).to eq "(`g`.`on=1; DROP TABLE countries` = 'two')"
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should escape field values when string based' do
|
217
|
+
query = { "one" => "two'; DROP TABLE countries;" }
|
218
|
+
expect(@generic_dal.build_where_ns(query,'h')).to eq "(`h`.`one` = 'two\\'; DROP TABLE countries;')"
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should not build include or exclude into queries' do
|
222
|
+
query = { "one" => 2, "include" => "subdivisions", "exclude" => "countries", "two"=>3 }
|
223
|
+
expect(@generic_dal.build_where_ns(query,'i')).to eq "(`i`.`one` = 2) AND (`i`.`two` = 3)"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#build_fields' do
|
228
|
+
it 'should return an empty string with no fields' do
|
229
|
+
expect(@generic_dal.build_select_fields([],nil)).to eq ""
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'should return fields correctly' do
|
233
|
+
expect(@generic_dal.build_select_fields(['one','two'],nil)).to eq "`one`,`two`"
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should return namespaced fields correctly' do
|
237
|
+
expect(@generic_dal.build_select_fields(['one','two'],'a')).to eq "`a`.`one`,`a`.`two`"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe '#build_fields' do
|
242
|
+
before(:each) do
|
243
|
+
@generic_dal.fields = {
|
244
|
+
"test1" => { :type=>:string },
|
245
|
+
"test2" => { :type=>:string },
|
246
|
+
"testX" => { :type=>:string },
|
247
|
+
}
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should return all fields with nil excludes' do
|
251
|
+
expect(@generic_dal.build_fields({})).to eq "`test1`,`test2`,`testX`"
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should return all fields with empty excludes' do
|
255
|
+
expect(@generic_dal.build_fields({"exclude"=>nil})).to eq "`test1`,`test2`,`testX`"
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should exclude a single field' do
|
259
|
+
expect(@generic_dal.build_fields({"exclude"=>'test1'})).to eq "`test2`,`testX`"
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should exclude multiple fields' do
|
263
|
+
expect(@generic_dal.build_fields({"exclude"=>'test1,testX'})).to eq "`test2`"
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#build_fields_with_ns' do
|
268
|
+
before(:each) do
|
269
|
+
@generic_dal.fields = {
|
270
|
+
"test1" => { :type=>:string },
|
271
|
+
"test2" => { :type=>:string },
|
272
|
+
"testX" => { :type=>:string },
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'should return all fields with nil excludes' do
|
277
|
+
expect(@generic_dal.build_fields_with_ns({},'a')).to eq "`a`.`test1`,`a`.`test2`,`a`.`testX`"
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should return all fields with empty excludes' do
|
281
|
+
expect(@generic_dal.build_fields_with_ns({"exclude"=>nil},'b')).to eq "`b`.`test1`,`b`.`test2`,`b`.`testX`"
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should exclude a single field' do
|
285
|
+
expect(@generic_dal.build_fields_with_ns({"exclude"=>'test1'},'c')).to eq "`c`.`test2`,`c`.`testX`"
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'should exclude multiple fields' do
|
289
|
+
expect(@generic_dal.build_fields_with_ns({"exclude"=>'test1,testX'},'d')).to eq "`d`.`test2`"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe '#get_includes' do
|
294
|
+
before(:each) do
|
295
|
+
@generic_dal.fields = ["test1", "test2", "testX"]
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should return an empty array with a nil query' do
|
299
|
+
expect(@generic_dal.get_includes(nil)).to eq []
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should return an empty array with no fields or includes' do
|
303
|
+
query = { }
|
304
|
+
expect(@generic_dal.get_includes(query)).to eq []
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'should return an empty array with fields and no includes' do
|
308
|
+
query = { "field2" => "xxas"}
|
309
|
+
expect(@generic_dal.get_includes(query)).to eq []
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'should return a single include' do
|
313
|
+
query = { "include"=>"test1" }
|
314
|
+
expect(@generic_dal.get_includes(query)).to eq ['test1']
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should return multiple includes' do
|
318
|
+
query = { "include"=>"test1,test2"}
|
319
|
+
expect(@generic_dal.get_includes(query)).to eq ['test1','test2']
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
describe '#get_excludes' do
|
324
|
+
before(:each) do
|
325
|
+
@generic_dal.fields = {
|
326
|
+
"test1" => { :type=>:string },
|
327
|
+
"test2" => { :type=>:string },
|
328
|
+
"testX" => { :type=>:string },
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'should return an empty array with a nil query' do
|
333
|
+
expect(@generic_dal.get_excludes(nil)).to eq []
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'should return an empty array with no fields or excludes' do
|
337
|
+
query = { }
|
338
|
+
expect(@generic_dal.get_excludes(query)).to eq []
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'should return an empty array with fields and no excludes' do
|
342
|
+
query = { "field2" => "xxas"}
|
343
|
+
expect(@generic_dal.get_excludes(query)).to eq []
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'should return a single exclude' do
|
347
|
+
query = { "exclude"=>"test1", "field2" => "xxas"}
|
348
|
+
expect(@generic_dal.get_excludes(query)).to eq ['test1']
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'should return multiple excludes' do
|
352
|
+
query = { "exclude"=>"test1,test2"}
|
353
|
+
expect(@generic_dal.get_excludes(query)).to eq ['test1','test2']
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe '#build_equal_condition' do
|
358
|
+
it 'should return IS NULL for a nil' do
|
359
|
+
expect(@generic_dal.build_equal_condition(nil)).to eq 'IS NULL'
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should return correct response for an integer' do
|
363
|
+
expect(@generic_dal.build_equal_condition(1)).to eq '= 1'
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'should return correct response for a float' do
|
367
|
+
expect(@generic_dal.build_equal_condition(1.123)).to eq '= 1.123'
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should return correct response for a string' do
|
371
|
+
expect(@generic_dal.build_equal_condition('ABC')).to eq "= 'ABC'"
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'should return correct escaped response for a string' do
|
375
|
+
expect(@generic_dal.build_equal_condition("AB'; DROP TABLE test_table --")).to eq "= 'AB\\'; DROP TABLE test_table --'"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe '#valid_query?' do
|
380
|
+
before(:each) do
|
381
|
+
@generic_dal.fields = {
|
382
|
+
"one" => { :type=>:string },
|
383
|
+
"two" => { :type=>:string },
|
384
|
+
"three" => { :type=>:string },
|
385
|
+
}
|
386
|
+
@generic_dal.relations = {
|
387
|
+
"four" => { :type=>:string },
|
388
|
+
"five" => { :type=>:string },
|
389
|
+
"six" => { :type=>:string },
|
390
|
+
}
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'should return true with valid fields' do
|
394
|
+
expect(@generic_dal.valid_query?({"one"=>1})).to be true
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'should return false with invalid fields' do
|
398
|
+
expect(@generic_dal.valid_query?({"five"=>1})).to be false
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'should return true with valid relations' do
|
402
|
+
expect(@generic_dal.valid_query?({"include"=>'four,five'})).to be true
|
403
|
+
end
|
404
|
+
|
405
|
+
it 'should return false with invalid relations' do
|
406
|
+
expect(@generic_dal.valid_query?({"include"=>'ten'})).to be false
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'should return false with nil' do
|
410
|
+
expect(@generic_dal.valid_query?(nil)).to be false
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'should return true with no fields' do
|
414
|
+
expect(@generic_dal.valid_query?({})).to be true
|
415
|
+
end
|
416
|
+
|
417
|
+
it 'should return true regardless of include' do
|
418
|
+
expect(@generic_dal.valid_query?({"one"=>1,"include"=>"two"})).to be true
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'should return true regardless of exclude' do
|
422
|
+
expect(@generic_dal.valid_query?({"one"=>1,"exclude"=>"one"})).to be true
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'should return false as cannot exclude a relation' do
|
426
|
+
expect(@generic_dal.valid_query?({"one"=>1,"exclude"=>"five"})).to be false
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe '#escape_str_field' do
|
431
|
+
it 'should escape single quotes' do
|
432
|
+
expect(@generic_dal.escape_str_field("ABC'BC")).to eq "ABC\\'BC"
|
433
|
+
end
|
434
|
+
|
435
|
+
it 'should remove backtics' do
|
436
|
+
expect(@generic_dal.escape_str_field("ABC`BC")).to eq "ABCBC"
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'should resolve symbols as well as strings' do
|
440
|
+
expect(@generic_dal.escape_str_field(:testing)).to eq "testing"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
describe '#get_all_by_query' do
|
445
|
+
it 'should call cached_query with the correct query for one field' do
|
446
|
+
memcache_null(@mock_memcache)
|
447
|
+
@generic_dal.fields = {
|
448
|
+
"one" => { :type=>:string },
|
449
|
+
"two" => { :type=>:string },
|
450
|
+
}
|
451
|
+
@generic_dal.table_name = 'test_table'
|
452
|
+
|
453
|
+
expect(@mock_mysql).to receive(:query).with("SELECT `one`,`two` FROM `test_table` WHERE (`field` = 'test2')")
|
454
|
+
|
455
|
+
@generic_dal.get_all_by_query({ :field => 'test2' })
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'should call cached_query with the correct query for multiple fields' do
|
459
|
+
memcache_null(@mock_memcache)
|
460
|
+
@generic_dal.fields = {
|
461
|
+
"one" => { :type=>:string },
|
462
|
+
"two" => { :type=>:string },
|
463
|
+
}
|
464
|
+
@generic_dal.table_name = 'test_table'
|
465
|
+
|
466
|
+
expect(@mock_mysql).to receive(:query).with("SELECT `one`,`two` FROM `test_table` WHERE (`field` = 'test2') AND (`twofield` = 2) AND (`nullfield` IS NULL)")
|
467
|
+
|
468
|
+
@generic_dal.get_all_by_query({ :field => 'test2', "twofield" =>2, "nullfield" => nil })
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe '#get_last_id' do
|
473
|
+
it 'should call mysql last_id' do
|
474
|
+
expect(@mock_mysql).to receive(:last_id)
|
475
|
+
@generic_dal.get_last_id
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
describe '#get_one' do
|
480
|
+
before(:each) do
|
481
|
+
memcache_null(@mock_memcache)
|
482
|
+
|
483
|
+
@generic_dal.fields = {
|
484
|
+
"one" => { :type=>:string },
|
485
|
+
"two" => { :type=>:string },
|
486
|
+
}
|
487
|
+
|
488
|
+
@generic_dal.table_name = 'test_table'
|
489
|
+
|
490
|
+
@mock_result = mysql_result_mock([
|
491
|
+
{ "field_one" => "one" },
|
492
|
+
{ "field_one" => "two" }
|
493
|
+
])
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'should call cached_query with the correct query for one field and return a single object' do
|
497
|
+
expect(@mock_mysql).to receive(:query)
|
498
|
+
.with("SELECT `one`,`two` FROM `test_table` WHERE (`field` = 'test2')")
|
499
|
+
.and_return(@mock_result)
|
500
|
+
|
501
|
+
expect(@generic_dal.get_one({ :field => 'test2' })).to eq({ "field_one" => "one" })
|
502
|
+
end
|
503
|
+
|
504
|
+
it 'should call cached_query with the correct query for one field and return a single object' do
|
505
|
+
expect(@mock_mysql).to receive(:query)
|
506
|
+
.with("SELECT `one`,`two` FROM `test_table` WHERE (`field` = 'test2') AND (`field_two` = 'test3')")
|
507
|
+
.and_return(@mock_result)
|
508
|
+
|
509
|
+
expect(@generic_dal.get_one({ :field => 'test2', :field_two => 'test3' })).to eq({ "field_one" => "one" })
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe '#map_to_hash_by_primary_key' do
|
514
|
+
before(:each) do
|
515
|
+
@generic_dal.primary_key = 'id'
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'should return an empty hash when given an empty array' do
|
519
|
+
test = []
|
520
|
+
|
521
|
+
expect(@generic_dal.map_to_hash_by_primary_key(test)).to eq({})
|
522
|
+
end
|
523
|
+
|
524
|
+
it 'should correctly map an array' do
|
525
|
+
test = [
|
526
|
+
{ "id" => 1, "field_one" => "one" },
|
527
|
+
{ "id" => 2.5, "field_one" => "two point five" },
|
528
|
+
{ "id" => "3", "field_one" => "three" },
|
529
|
+
{ "id" => nil, "field_one" => "four" }
|
530
|
+
]
|
531
|
+
|
532
|
+
expect(@generic_dal.map_to_hash_by_primary_key(test)).to eq({
|
533
|
+
1 => { "id" => 1, "field_one" => "one" },
|
534
|
+
2.5 => { "id" => 2.5, "field_one" => "two point five" },
|
535
|
+
"3" => { "id" => "3", "field_one" => "three" },
|
536
|
+
nil => { "id" => nil, "field_one" => "four" }
|
537
|
+
})
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
describe '#remove_key_from_hash_of_arrays!' do
|
542
|
+
|
543
|
+
it 'should remove a key from each hash in each array in each hash value' do
|
544
|
+
|
545
|
+
test = {
|
546
|
+
'one' => [ ],
|
547
|
+
2 => [ {"x" => 'a', "y" => 'b', 'z' => 'c' } ],
|
548
|
+
nil => [ {"x" => 'd', "y" => 'e', 'z' => 'f' }, {"x" => 'g', "y" => 'h', 'z' => 'i' } ],
|
549
|
+
}
|
550
|
+
|
551
|
+
@generic_dal.remove_key_from_hash_of_arrays!(test,'z')
|
552
|
+
|
553
|
+
expect(test).to eq({
|
554
|
+
'one' => [ ],
|
555
|
+
2 => [ {"x" => 'a', "y" => 'b'} ],
|
556
|
+
nil => [ {"x" => 'd', "y" => 'e' }, {"x" => 'g', "y" => 'h' } ],
|
557
|
+
})
|
558
|
+
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
describe '#map_to_hash_of_arrays_by_key' do
|
563
|
+
it 'should return an empty hash when given an empty array' do
|
564
|
+
test = []
|
565
|
+
|
566
|
+
expect(@generic_dal.map_to_hash_of_arrays_by_key(test,'field_one')).to eq({})
|
567
|
+
end
|
568
|
+
|
569
|
+
it 'should correctly map an array' do
|
570
|
+
test = [
|
571
|
+
{ "id" => 1, "field_one" => 1 },
|
572
|
+
{ "id" => 2.5, "field_one" => "two point five" },
|
573
|
+
{ "id" => "3", "field_one" => "three" },
|
574
|
+
{ "id" => nil, "field_one" => 4.5 },
|
575
|
+
{ "id" => nil, "field_one" => 1 },
|
576
|
+
{ "id" => 90, "field_one" => "two point five" },
|
577
|
+
{ "id" => nil, "field_one" => "four" },
|
578
|
+
{ "id" => "16", "field_one" => "three" },
|
579
|
+
{ "id" => 2.1, "field_one" => 4.5 },
|
580
|
+
{ "id" => 328, "field_one" => "one" },
|
581
|
+
{ "id" => nil, "field_one" => nil },
|
582
|
+
{ "id" => 123, "field_one" => nil },
|
583
|
+
]
|
584
|
+
|
585
|
+
expect(@generic_dal.map_to_hash_of_arrays_by_key(test,'field_one')).to eq({
|
586
|
+
nil => [
|
587
|
+
{ "id" => nil, "field_one" => nil },
|
588
|
+
{ "id" => 123, "field_one" => nil },
|
589
|
+
],
|
590
|
+
1 => [
|
591
|
+
{ "id" => 1, "field_one" => 1 },
|
592
|
+
{ "id" => nil, "field_one" => 1 },
|
593
|
+
],
|
594
|
+
"two point five" => [
|
595
|
+
{ "id" => 2.5, "field_one" => "two point five" },
|
596
|
+
{ "id" => 90, "field_one" => "two point five" },
|
597
|
+
],
|
598
|
+
"three" => [
|
599
|
+
{ "id" => "3", "field_one" => "three" },
|
600
|
+
{ "id" => "16", "field_one" => "three" },
|
601
|
+
],
|
602
|
+
"four" => [
|
603
|
+
{ "id" => nil, "field_one" => "four" },
|
604
|
+
],
|
605
|
+
4.5 => [
|
606
|
+
{ "id" => nil, "field_one" => 4.5 },
|
607
|
+
{ "id" => 2.1, "field_one" => 4.5 },
|
608
|
+
],
|
609
|
+
"one" => [
|
610
|
+
{ "id" => 328, "field_one" => "one" },
|
611
|
+
]
|
612
|
+
})
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
describe '#add_field_from_map!' do
|
617
|
+
it 'should map correctly' do
|
618
|
+
records = [
|
619
|
+
{"id"=>1, "fk_code"=>"EU", "name"=>"Test1" },
|
620
|
+
{"id"=>2, "fk_code"=>"EU", "name"=>"Test2" },
|
621
|
+
{"id"=>3, "fk_code"=>"AU", "name"=>"Test3" },
|
622
|
+
{"id"=>4, "fk_code"=>"GB", "name"=>"Test4" },
|
623
|
+
{"id"=>5, "fk_code"=>"US", "name"=>"Test5" },
|
624
|
+
{"id"=>6, "fk_code"=>nil, "name"=>"Test5" },
|
625
|
+
]
|
626
|
+
|
627
|
+
map = {
|
628
|
+
'EU' => 1,
|
629
|
+
'AU' => { "name"=>"one" },
|
630
|
+
'US' => nil,
|
631
|
+
'GB' => "test!"
|
632
|
+
}
|
633
|
+
|
634
|
+
@generic_dal.add_field_from_map!(records, map, 'fk_field', 'fk_code')
|
635
|
+
|
636
|
+
expect(records).to eq [
|
637
|
+
{"id"=>1, "fk_code"=>"EU", "name"=>"Test1", "fk_field"=>1 },
|
638
|
+
{"id"=>2, "fk_code"=>"EU", "name"=>"Test2", "fk_field"=>1 },
|
639
|
+
{"id"=>3, "fk_code"=>"AU", "name"=>"Test3", "fk_field"=>{ "name"=>"one" } },
|
640
|
+
{"id"=>4, "fk_code"=>"GB", "name"=>"Test4", "fk_field"=>"test!" },
|
641
|
+
{"id"=>5, "fk_code"=>"US", "name"=>"Test5", "fk_field"=>nil },
|
642
|
+
{"id"=>6, "fk_code"=>nil, "name"=>"Test5" },
|
643
|
+
]
|
644
|
+
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
describe '#get_relation_query_sql' do
|
649
|
+
it 'should return the correct sql for a has_one relation with no query' do
|
650
|
+
|
651
|
+
@generic_dal.table_name = "currencies"
|
652
|
+
|
653
|
+
rel = {
|
654
|
+
:type => :has_one,
|
655
|
+
:table => 'countries',
|
656
|
+
:table_key => 'default_currency_code',
|
657
|
+
:this_key => 'code',
|
658
|
+
:table_fields => 'code_alpha_2,name',
|
659
|
+
}
|
660
|
+
|
661
|
+
expect(@generic_dal.get_relation_query_sql(rel,{})).to eq(
|
662
|
+
"SELECT `a`.`code_alpha_2`,`a`.`name`,`b`.`code` AS `_table_key` FROM `countries` AS `a`, `currencies` AS `b` WHERE (`a`.`default_currency_code` = `b`.`code`)"
|
663
|
+
)
|
664
|
+
|
665
|
+
end
|
666
|
+
|
667
|
+
it 'should return the correct sql for a has_one relation with a query' do
|
668
|
+
|
669
|
+
@generic_dal.table_name = "currencies"
|
670
|
+
|
671
|
+
rel = {
|
672
|
+
:type => :has_one,
|
673
|
+
:table => 'countries',
|
674
|
+
:table_key => 'default_currency_code',
|
675
|
+
:this_key => 'code',
|
676
|
+
:table_fields => 'code_alpha_2,name',
|
677
|
+
}
|
678
|
+
|
679
|
+
expect(@generic_dal.get_relation_query_sql(rel,{'testfield'=>1})).to eq(
|
680
|
+
"SELECT `a`.`code_alpha_2`,`a`.`name`,`b`.`code` AS `_table_key` FROM `countries` AS `a`, `currencies` AS `b` WHERE (`a`.`default_currency_code` = `b`.`code`) AND (`b`.`testfield` = 1)"
|
681
|
+
)
|
682
|
+
|
683
|
+
end
|
684
|
+
|
685
|
+
it 'should return the correct sql for a has_many relation' do
|
686
|
+
|
687
|
+
@generic_dal.table_name = "houses"
|
688
|
+
|
689
|
+
rel = {
|
690
|
+
:type => :has_many,
|
691
|
+
:table => 'cats',
|
692
|
+
:table_key => 'house_id',
|
693
|
+
:this_key => 'id',
|
694
|
+
:table_fields => 'cat_id,name',
|
695
|
+
}
|
696
|
+
|
697
|
+
expect(@generic_dal.get_relation_query_sql(rel,{})).to eq(
|
698
|
+
"SELECT `a`.`cat_id`,`a`.`name`,`b`.`id` AS `_table_key` FROM `cats` AS `a`, `houses` AS `b` WHERE (`a`.`house_id` = `b`.`id`)"
|
699
|
+
)
|
700
|
+
|
701
|
+
end
|
702
|
+
|
703
|
+
it 'should return the correct sql for a has_many relation with a query' do
|
704
|
+
|
705
|
+
@generic_dal.table_name = "houses"
|
706
|
+
|
707
|
+
rel = {
|
708
|
+
:type => :has_many,
|
709
|
+
:table => 'cats',
|
710
|
+
:table_key => 'house_id',
|
711
|
+
:this_key => 'id',
|
712
|
+
:table_fields => 'cat_id,name',
|
713
|
+
}
|
714
|
+
|
715
|
+
expect(@generic_dal.get_relation_query_sql(rel,{"colour"=>"ginger"})).to eq(
|
716
|
+
"SELECT `a`.`cat_id`,`a`.`name`,`b`.`id` AS `_table_key` FROM `cats` AS `a`, `houses` AS `b` WHERE (`a`.`house_id` = `b`.`id`) AND (`b`.`colour` = 'ginger')"
|
717
|
+
)
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
it 'should return the correct sql for a has_many_through relation' do
|
722
|
+
|
723
|
+
@generic_dal.table_name = "countries"
|
724
|
+
|
725
|
+
rel = {
|
726
|
+
:type => :has_many_through,
|
727
|
+
:table => 'regions',
|
728
|
+
:link_table => 'region_countries',
|
729
|
+
:link_key => 'country_code_alpha_2',
|
730
|
+
:link_field => 'region_code',
|
731
|
+
:table_key => 'code',
|
732
|
+
:this_key => 'code_alpha_2',
|
733
|
+
:table_fields => 'code,name',
|
734
|
+
}
|
735
|
+
|
736
|
+
expect(@generic_dal.get_relation_query_sql(rel,{})).to eq(
|
737
|
+
"SELECT `a`.`code`,`a`.`name`,`c`.`code_alpha_2` AS `_table_key` FROM `regions` AS `a`, `region_countries` AS `b`, `countries` AS `c` WHERE (`a`.`code` = `b`.`region_code` AND `b`.`country_code_alpha_2` = `c`.`code_alpha_2`)"
|
738
|
+
)
|
739
|
+
|
740
|
+
end
|
741
|
+
|
742
|
+
it 'should return the correct sql for a has_many_through relation with a query' do
|
743
|
+
|
744
|
+
@generic_dal.table_name = "countries"
|
745
|
+
|
746
|
+
rel = {
|
747
|
+
:type => :has_many_through,
|
748
|
+
:table => 'regions',
|
749
|
+
:link_table => 'region_countries',
|
750
|
+
:link_key => 'country_code_alpha_2',
|
751
|
+
:link_field => 'region_code',
|
752
|
+
:table_key => 'code',
|
753
|
+
:this_key => 'code_alpha_2',
|
754
|
+
:table_fields => 'code,name',
|
755
|
+
}
|
756
|
+
|
757
|
+
expect(@generic_dal.get_relation_query_sql(rel,{"default_currency_code"=>"EUR"})).to eq(
|
758
|
+
"SELECT `a`.`code`,`a`.`name`,`c`.`code_alpha_2` AS `_table_key` FROM `regions` AS `a`, `region_countries` AS `b`, `countries` AS `c` WHERE (`a`.`code` = `b`.`region_code` AND `b`.`country_code_alpha_2` = `c`.`code_alpha_2`) AND (`c`.`default_currency_code` = 'EUR')"
|
759
|
+
)
|
760
|
+
|
761
|
+
end
|
762
|
+
|
763
|
+
it 'should return the correct sql for a has_many_through relation with a query' do
|
764
|
+
|
765
|
+
@generic_dal.table_name = "countries"
|
766
|
+
|
767
|
+
rel = {
|
768
|
+
:type => :unknown,
|
769
|
+
:table => 'regions',
|
770
|
+
:link_table => 'region_countries',
|
771
|
+
:link_key => 'country_code_alpha_2',
|
772
|
+
:link_field => 'region_code',
|
773
|
+
:table_key => 'code',
|
774
|
+
:this_key => 'code_alpha_2',
|
775
|
+
:table_fields => 'code,name',
|
776
|
+
}
|
777
|
+
|
778
|
+
expect(@mock_log).to receive(:error).with("Relation type unknown undefined!")
|
779
|
+
|
780
|
+
expect(@generic_dal.get_relation_query_sql(rel,{"default_currency_code"=>"EUR"})).to be_nil()
|
781
|
+
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
785
|
+
describe "#get_relation_tables" do
|
786
|
+
it 'should return the correct tables for a has_one relation' do
|
787
|
+
@generic_dal.table_name = "currencies"
|
788
|
+
|
789
|
+
rel = {
|
790
|
+
:type => :has_one,
|
791
|
+
:table => 'countries',
|
792
|
+
:table_key => 'default_currency_code',
|
793
|
+
:this_key => 'code',
|
794
|
+
:table_fields => 'code_alpha_2,name',
|
795
|
+
}
|
796
|
+
|
797
|
+
expect(@generic_dal.get_relation_tables(rel)).to eq(["countries", "currencies"])
|
798
|
+
end
|
799
|
+
|
800
|
+
it 'should return the correct tables for a has_many relation' do
|
801
|
+
@generic_dal.table_name = "houses"
|
802
|
+
|
803
|
+
rel = {
|
804
|
+
:type => :has_many,
|
805
|
+
:table => 'cats',
|
806
|
+
:table_key => 'house_id',
|
807
|
+
:this_key => 'id',
|
808
|
+
:table_fields => 'cat_id,name',
|
809
|
+
}
|
810
|
+
|
811
|
+
expect(@generic_dal.get_relation_tables(rel)).to eq(["cats", "houses"])
|
812
|
+
end
|
813
|
+
|
814
|
+
it 'should return the correct tables for a has_many_through relation' do
|
815
|
+
@generic_dal.table_name = "countries"
|
816
|
+
|
817
|
+
rel = {
|
818
|
+
:type => :has_many_through,
|
819
|
+
:table => 'regions',
|
820
|
+
:link_table => 'region_countries',
|
821
|
+
:link_key => 'country_code_alpha_2',
|
822
|
+
:link_field => 'region_code',
|
823
|
+
:table_key => 'code',
|
824
|
+
:this_key => 'code_alpha_2',
|
825
|
+
:table_fields => 'code,name',
|
826
|
+
}
|
827
|
+
|
828
|
+
expect(@generic_dal.get_relation_tables(rel)).to eq(["countries","region_countries","regions"])
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'should throw if unknown relation' do
|
832
|
+
@generic_dal.table_name = "countries"
|
833
|
+
|
834
|
+
rel = {
|
835
|
+
:type => :unknown,
|
836
|
+
}
|
837
|
+
|
838
|
+
expect{@generic_dal.get_relation_tables(rel)}.to raise_error("Unknown Relation type unknown")
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
describe '#expire_table_cache' do
|
843
|
+
it 'should set a table version when it doesnt exist' do
|
844
|
+
|
845
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(nil)
|
846
|
+
expect(@mock_memcache).to receive(:set).ordered.with("prefix-testtable-version",1,nil,{:raw=>true}).and_return(nil)
|
847
|
+
|
848
|
+
@generic_dal.expire_table_cache(['testtable'])
|
849
|
+
end
|
850
|
+
|
851
|
+
it 'should increment a table version when it exists' do
|
852
|
+
|
853
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
854
|
+
expect(@mock_memcache).to receive(:incr).ordered.with("prefix-testtable-version",1,nil).and_return(nil)
|
855
|
+
|
856
|
+
@generic_dal.expire_table_cache(['testtable'])
|
857
|
+
end
|
858
|
+
|
859
|
+
it 'should expire multiple tables' do
|
860
|
+
|
861
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-testtable-version").and_return(1)
|
862
|
+
expect(@mock_memcache).to receive(:incr).ordered.with("prefix-testtable-version",1,nil).and_return(nil)
|
863
|
+
expect(@mock_memcache).to receive(:get).ordered.with("prefix-tabletwo-version").and_return(1)
|
864
|
+
expect(@mock_memcache).to receive(:incr).ordered.with("prefix-tabletwo-version",1,nil).and_return(nil)
|
865
|
+
|
866
|
+
@generic_dal.expire_table_cache(['testtable','tabletwo'])
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
describe '#exists_by_primary_key?' do
|
871
|
+
before do
|
872
|
+
memcache_null(@mock_memcache)
|
873
|
+
|
874
|
+
@generic_dal.table_name = 'pktesttable'
|
875
|
+
@generic_dal.primary_key = 'id'
|
876
|
+
|
877
|
+
@mock_result = mysql_result_mock([ { "c" => 1 } ])
|
878
|
+
end
|
879
|
+
|
880
|
+
it 'should call cached_query with correct sql with a numeric primary key' do
|
881
|
+
expect(@mock_mysql).to receive(:query).with("SELECT COUNT(*) AS `c` FROM `pktesttable` WHERE (`id` = 2002)").and_return(@mock_result)
|
882
|
+
|
883
|
+
expect(@generic_dal.exists_by_primary_key?(2002)).to eq(true)
|
884
|
+
end
|
885
|
+
|
886
|
+
it 'should call cached_query with correct sql with a string primary key' do
|
887
|
+
expect(@mock_mysql).to receive(:query).with("SELECT COUNT(*) AS `c` FROM `pktesttable` WHERE (`id` = 'test')").and_return(@mock_result)
|
888
|
+
|
889
|
+
expect(@generic_dal.exists_by_primary_key?('test')).to eq(true)
|
890
|
+
end
|
891
|
+
|
892
|
+
it 'should return true when count is not 0' do
|
893
|
+
@mock_result = mysql_result_mock([ { "c" => 1 } ])
|
894
|
+
|
895
|
+
expect(@mock_mysql).to receive(:query).with("SELECT COUNT(*) AS `c` FROM `pktesttable` WHERE (`id` = 'test')").and_return(@mock_result)
|
896
|
+
|
897
|
+
expect(@generic_dal.exists_by_primary_key?('test')).to eq(true)
|
898
|
+
end
|
899
|
+
|
900
|
+
it 'should return false when count is 0' do
|
901
|
+
@mock_result = mysql_result_mock([ { "c" => 0 } ])
|
902
|
+
|
903
|
+
expect(@mock_mysql).to receive(:query).with("SELECT COUNT(*) AS `c` FROM `pktesttable` WHERE (`id` = 'test')").and_return(@mock_result)
|
904
|
+
|
905
|
+
expect(@generic_dal.exists_by_primary_key?('test')).to eq(false)
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
909
|
+
describe '#valid_insert?' do
|
910
|
+
it 'should return false if object nil' do
|
911
|
+
expect(@generic_dal.valid_insert?(nil)).to eq(false)
|
912
|
+
end
|
913
|
+
|
914
|
+
it 'should return false if object empty' do
|
915
|
+
expect(@generic_dal.valid_insert?({})).to eq(false)
|
916
|
+
end
|
917
|
+
|
918
|
+
it 'should return true if all fields exist' do
|
919
|
+
@generic_dal.fields = {
|
920
|
+
"one" => { :type=>:string },
|
921
|
+
"two" => { :type=>:string },
|
922
|
+
}
|
923
|
+
|
924
|
+
expect(@generic_dal.valid_insert?({ "one"=>"1", "two"=>"2" })).to eq(true)
|
925
|
+
end
|
926
|
+
|
927
|
+
it 'should return false if fields do not exist' do
|
928
|
+
@generic_dal.fields = {
|
929
|
+
"one" => { :type=>:string },
|
930
|
+
"two" => { :type=>:string },
|
931
|
+
}
|
932
|
+
|
933
|
+
expect(@generic_dal.valid_insert?({ "five"=>"1", "two"=>"2" })).to eq(false)
|
934
|
+
end
|
935
|
+
|
936
|
+
it 'should return true if data is within the max length' do
|
937
|
+
@generic_dal.fields = {
|
938
|
+
"one" => { :type=>:string },
|
939
|
+
"two" => { :type=>:string, :length=>4 },
|
940
|
+
}
|
941
|
+
|
942
|
+
expect(@generic_dal.valid_insert?({ "one"=>"1", "two"=>"2" })).to eq(true)
|
943
|
+
end
|
944
|
+
|
945
|
+
it 'should return false if data is greater than the max length' do
|
946
|
+
@generic_dal.fields = {
|
947
|
+
"one" => { :type=>:string },
|
948
|
+
"two" => { :type=>:string, :length=>4 },
|
949
|
+
}
|
950
|
+
|
951
|
+
expect(@generic_dal.valid_insert?({ "one"=>"1", "two"=>"22332" })).to eq(false)
|
952
|
+
end
|
953
|
+
|
954
|
+
it 'should return false if required key is missing' do
|
955
|
+
@generic_dal.fields = {
|
956
|
+
"one" => { :type=>:string },
|
957
|
+
"two" => { :type=>:string, :required=>true },
|
958
|
+
}
|
959
|
+
|
960
|
+
expect(@generic_dal.valid_insert?({ "one"=>"1" })).to eq(false)
|
961
|
+
end
|
962
|
+
|
963
|
+
it 'should return true if required keys are ok' do
|
964
|
+
@generic_dal.fields = {
|
965
|
+
"one" => { :type=>:string, :required=>true },
|
966
|
+
"two" => { :type=>:string, :required=>true },
|
967
|
+
}
|
968
|
+
|
969
|
+
expect(@generic_dal.valid_insert?({ "one"=>"1","two"=>"2" })).to eq(true)
|
970
|
+
end
|
971
|
+
end
|
972
|
+
|
973
|
+
describe '#valid_update?' do
|
974
|
+
it 'should return false if object nil' do
|
975
|
+
expect(@generic_dal.valid_update?(nil)).to eq(false)
|
976
|
+
end
|
977
|
+
|
978
|
+
it 'should return false if object empty' do
|
979
|
+
expect(@generic_dal.valid_update?({})).to eq(false)
|
980
|
+
end
|
981
|
+
|
982
|
+
it 'should return true if all fields exist' do
|
983
|
+
@generic_dal.fields = {
|
984
|
+
"one" => { :type=>:string },
|
985
|
+
"two" => { :type=>:string },
|
986
|
+
}
|
987
|
+
|
988
|
+
expect(@generic_dal.valid_update?({ "one"=>"1", "two"=>"2" })).to eq(true)
|
989
|
+
end
|
990
|
+
|
991
|
+
it 'should return false if fields do not exist' do
|
992
|
+
@generic_dal.fields = {
|
993
|
+
"one" => { :type=>:string },
|
994
|
+
"two" => { :type=>:string },
|
995
|
+
}
|
996
|
+
|
997
|
+
expect(@generic_dal.valid_update?({ "five"=>"1", "two"=>"2" })).to eq(false)
|
998
|
+
end
|
999
|
+
|
1000
|
+
it 'should return false if data is greater than the max length' do
|
1001
|
+
@generic_dal.fields = {
|
1002
|
+
"one" => { :type=>:string },
|
1003
|
+
"two" => { :type=>:string, :length=>4 },
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
expect(@generic_dal.valid_update?({ "one"=>"1", "two"=>"22332" })).to eq(false)
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
describe "#escape_value" do
|
1011
|
+
it 'should return NULL for nil' do
|
1012
|
+
expect(@generic_dal.escape_value(nil)).to eq('NULL')
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
it 'should return integer for int/float' do
|
1016
|
+
expect(@generic_dal.escape_value(1)).to eq('1')
|
1017
|
+
expect(@generic_dal.escape_value(1.45)).to eq('1.45')
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
it 'should return a quoted string for string' do
|
1021
|
+
expect(@generic_dal.escape_value('test')).to eq("'test'")
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
it 'should escape sql values properly' do
|
1025
|
+
expect(@generic_dal.escape_value("test '; DROP TABLE test; --")).to eq("'test \\'; DROP TABLE test; --'")
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
describe "#build_insert" do
|
1030
|
+
it 'should return correct SQL fragment for basic fields' do
|
1031
|
+
data = {
|
1032
|
+
"one" => 1,
|
1033
|
+
"two" => "2",
|
1034
|
+
"three" => nil,
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
expect(@generic_dal.build_insert(data)).to eq("(`one`, `two`, `three`) VALUES (1, '2', NULL)")
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
it 'should escape field names and data' do
|
1041
|
+
data = {
|
1042
|
+
"one`; DROP TABLE test; -- " => 1,
|
1043
|
+
"two" => "two",
|
1044
|
+
"three" => "'; DROP TABLE test; --'",
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
expect(@generic_dal.build_insert(data)).to eq("(`one; DROP TABLE test; -- `, `two`, `three`) VALUES (1, 'two', '\\'; DROP TABLE test; --\\'')")
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
describe "#build_update" do
|
1052
|
+
it 'should return correct SQL fragment for basic fields' do
|
1053
|
+
data = {
|
1054
|
+
"one" => 1,
|
1055
|
+
"two" => "two",
|
1056
|
+
"three" => nil,
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
expect(@generic_dal.build_update(data)).to eq("`one` = 1, `two` = 'two', `three` = NULL")
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
it 'should escape field names and data' do
|
1063
|
+
data = {
|
1064
|
+
"one`; DROP TABLE test; -- " => 1,
|
1065
|
+
"two" => "2",
|
1066
|
+
"three" => "'; DROP TABLE test; --'",
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
expect(@generic_dal.build_update(data)).to eq("`one; DROP TABLE test; -- ` = 1, `two` = '2', `three` = '\\'; DROP TABLE test; --\\''")
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
describe "#get_all_related_tables" do
|
1074
|
+
it 'should return the table name for nil relations' do
|
1075
|
+
@generic_dal.table_name = 'test1'
|
1076
|
+
|
1077
|
+
@generic_dal.relations = nil
|
1078
|
+
|
1079
|
+
expect(@generic_dal.get_all_related_tables).to eq(["test1"])
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
it 'should return the table name for empty relations' do
|
1083
|
+
@generic_dal.table_name = 'test1'
|
1084
|
+
|
1085
|
+
@generic_dal.relations = {}
|
1086
|
+
|
1087
|
+
expect(@generic_dal.get_all_related_tables).to eq(["test1"])
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
it 'should return the table name for a single relations' do
|
1091
|
+
@generic_dal.table_name = 'test1'
|
1092
|
+
|
1093
|
+
@generic_dal.relations = {
|
1094
|
+
'countries' => {
|
1095
|
+
:type => :has_one,
|
1096
|
+
:table => 'countries',
|
1097
|
+
:table_key => 'default_currency_code',
|
1098
|
+
:this_key => 'code',
|
1099
|
+
:table_fields => 'code_alpha_2,name',
|
1100
|
+
},
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
expect(@generic_dal.get_all_related_tables).to eq(["countries","test1"])
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
|
1107
|
+
it 'should return the correct table names for multiple relations with dedupe' do
|
1108
|
+
@generic_dal.table_name = 'test1'
|
1109
|
+
|
1110
|
+
@generic_dal.relations = {
|
1111
|
+
'countries' => {
|
1112
|
+
:type => :has_one,
|
1113
|
+
:table => 'countries',
|
1114
|
+
:table_key => 'default_currency_code',
|
1115
|
+
:this_key => 'code',
|
1116
|
+
:table_fields => 'code_alpha_2,name',
|
1117
|
+
},
|
1118
|
+
'countries2' => {
|
1119
|
+
:type => :has_many,
|
1120
|
+
:table => 'countries',
|
1121
|
+
:table_key => 'default_currency_code',
|
1122
|
+
:this_key => 'code',
|
1123
|
+
:table_fields => 'code_alpha_2,name',
|
1124
|
+
},
|
1125
|
+
'regions' => {
|
1126
|
+
:type => :has_many_through,
|
1127
|
+
:table => 'regions',
|
1128
|
+
:link_table => 'region_countries',
|
1129
|
+
:link_key => 'country_code_alpha_2',
|
1130
|
+
:link_field => 'region_code',
|
1131
|
+
:table_key => 'code',
|
1132
|
+
:this_key => 'code_alpha_2',
|
1133
|
+
:table_fields => 'code,name',
|
1134
|
+
}
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
expect(@generic_dal.get_all_related_tables).to eq(["countries", "region_countries", "regions", "test1"])
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
describe '#insert' do
|
1142
|
+
it 'should call the correct sql and expire the correct cache' do
|
1143
|
+
testdata = { "field_one" => "one" }
|
1144
|
+
|
1145
|
+
@generic_dal.table_name = "test_table"
|
1146
|
+
@generic_dal.fields = {
|
1147
|
+
"field_one" => { :type => :integer }
|
1148
|
+
}
|
1149
|
+
@generic_dal.auto_primary_key = false
|
1150
|
+
|
1151
|
+
query = "INSERT INTO `test_table` (`field_one`) VALUES ('one')"
|
1152
|
+
|
1153
|
+
expect(@mock_mysql).to receive(:query).ordered.with(query)
|
1154
|
+
expect(@mock_mysql).not_to receive(:last_id)
|
1155
|
+
|
1156
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1157
|
+
expect(@mock_memcache).to receive(:incr).ordered.with('prefix-test_table-version',1,nil)
|
1158
|
+
|
1159
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1160
|
+
expect(@mock_memcache).to receive(:get).ordered.and_return([{ "field_one" => "one","id"=>1 }])
|
1161
|
+
|
1162
|
+
@generic_dal.insert(testdata)
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it 'should call last_id when auto_primary_key is true' do
|
1166
|
+
testdata = { "field_one" => "one" }
|
1167
|
+
|
1168
|
+
@generic_dal.table_name = "test_table"
|
1169
|
+
@generic_dal.fields = {
|
1170
|
+
"field_one" => { :type => :integer }
|
1171
|
+
}
|
1172
|
+
@generic_dal.auto_primary_key = true
|
1173
|
+
|
1174
|
+
query = "INSERT INTO `test_table` (`field_one`) VALUES ('one')"
|
1175
|
+
|
1176
|
+
expect(@mock_mysql).to receive(:query).ordered.with(query)
|
1177
|
+
expect(@mock_mysql).to receive(:last_id)
|
1178
|
+
|
1179
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1180
|
+
expect(@mock_memcache).to receive(:incr).ordered.with('prefix-test_table-version',1,nil)
|
1181
|
+
|
1182
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1183
|
+
expect(@mock_memcache).to receive(:get).ordered.and_return([{ "field_one" => "one","id"=>1 }])
|
1184
|
+
expect(@mock_memcache).not_to receive(:last_id)
|
1185
|
+
|
1186
|
+
@generic_dal.insert(testdata)
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
it 'should log to error when insert fails' do
|
1190
|
+
testdata = { "field_one" => "one" }
|
1191
|
+
|
1192
|
+
@generic_dal.table_name = "test_table"
|
1193
|
+
@generic_dal.fields = {
|
1194
|
+
"field_one" => { :type => :integer }
|
1195
|
+
}
|
1196
|
+
@generic_dal.auto_primary_key = true
|
1197
|
+
|
1198
|
+
query = "INSERT INTO `test_table` (`field_one`) VALUES ('one')"
|
1199
|
+
|
1200
|
+
expect(@mock_mysql).to receive(:query) { raise "TestException" }
|
1201
|
+
|
1202
|
+
expect(@generic_dal).not_to receive(:get_one)
|
1203
|
+
expect(@mock_log).to receive(:error).with("TestException")
|
1204
|
+
|
1205
|
+
expect(@generic_dal.insert(testdata)).to be_nil()
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
describe '#update_by_primary_key' do
|
1210
|
+
it 'should call the correct sql and expire the correct cache' do
|
1211
|
+
testdata = { "field_one" => "two" }
|
1212
|
+
|
1213
|
+
@generic_dal.table_name = "test_table"
|
1214
|
+
@generic_dal.primary_key = "code"
|
1215
|
+
@generic_dal.fields = {
|
1216
|
+
"field_one" => { :type => :integer }
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
query = "UPDATE `test_table` SET `field_one` = 'two' WHERE (`code` = 2)"
|
1220
|
+
|
1221
|
+
expect(@mock_mysql).to receive(:query).ordered.with(query)
|
1222
|
+
|
1223
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1224
|
+
expect(@mock_memcache).to receive(:incr).ordered.with('prefix-test_table-version',1,nil)
|
1225
|
+
|
1226
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1227
|
+
expect(@mock_memcache).to receive(:get).ordered.and_return([{ "field_one" => "two","id"=>2}])
|
1228
|
+
|
1229
|
+
@generic_dal.update_by_primary_key(2, testdata)
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
it 'should log to error when db update fails' do
|
1233
|
+
testdata = { "field_one" => "two" }
|
1234
|
+
|
1235
|
+
@generic_dal.table_name = "test_table"
|
1236
|
+
@generic_dal.primary_key = "code"
|
1237
|
+
@generic_dal.fields = {
|
1238
|
+
"field_one" => { :type => :integer }
|
1239
|
+
}
|
1240
|
+
|
1241
|
+
query = "UPDATE `test_table` SET `field_one` = 'two' WHERE (`code` = 2)"
|
1242
|
+
|
1243
|
+
expect(@mock_mysql).to receive(:query) { raise "TestException" }
|
1244
|
+
expect(@generic_dal).not_to receive(:get_one)
|
1245
|
+
expect(@mock_log).to receive(:error).with("TestException")
|
1246
|
+
|
1247
|
+
expect(@generic_dal.update_by_primary_key(2, testdata)).to eq(false)
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
describe '#delete_by_primary_key' do
|
1252
|
+
it 'should call the correct sql and expire the correct cache' do
|
1253
|
+
|
1254
|
+
@generic_dal.table_name = "test_table"
|
1255
|
+
@generic_dal.primary_key = "code"
|
1256
|
+
|
1257
|
+
query = "DELETE FROM `test_table` WHERE (`code` = 'three')"
|
1258
|
+
|
1259
|
+
expect(@mock_mysql).to receive(:query).ordered.with(query)
|
1260
|
+
expect(@mock_memcache).to receive(:get).ordered.with('prefix-test_table-version').and_return(1)
|
1261
|
+
expect(@mock_memcache).to receive(:incr).ordered.with('prefix-test_table-version',1,nil)
|
1262
|
+
|
1263
|
+
@generic_dal.delete_by_primary_key('three')
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
it 'should log to error when db delete fails' do
|
1267
|
+
|
1268
|
+
@generic_dal.table_name = "test_table"
|
1269
|
+
@generic_dal.primary_key = "code"
|
1270
|
+
|
1271
|
+
query = "DELETE FROM `test_table` WHERE (`code` = 'three')"
|
1272
|
+
|
1273
|
+
expect(@mock_mysql).to receive(:query) { raise "TestException" }
|
1274
|
+
expect(@generic_dal).not_to receive(:get_one)
|
1275
|
+
expect(@mock_log).to receive(:error).with("TestException")
|
1276
|
+
|
1277
|
+
expect(@generic_dal.delete_by_primary_key('three')).to eq(false)
|
1278
|
+
end
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
describe '#get_all_by_query_as_hash' do
|
1282
|
+
it 'should call map_to_hash_by_primary_key / get_all_by_query from query' do
|
1283
|
+
|
1284
|
+
expect(@generic_dal).to receive(:get_all_by_query).with("query").and_return("results")
|
1285
|
+
expect(@generic_dal).to receive(:map_to_hash_by_primary_key).with("results").and_return("hashed_results")
|
1286
|
+
expect(@generic_dal.get_all_by_query_as_hash("query")).to eq("hashed_results")
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
describe '#get_relation_data_as_hash' do
|
1291
|
+
it 'should return {} if no relations exist' do
|
1292
|
+
@generic_dal.relations = nil
|
1293
|
+
expect(@generic_dal.get_relation_data_as_hash("query")).to eq({})
|
1294
|
+
@generic_dal.relations = {}
|
1295
|
+
expect(@generic_dal.get_relation_data_as_hash("query")).to eq({})
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
it 'should call get_includes' do
|
1299
|
+
@generic_dal.relations = { "name" => OpenStruct.new()}
|
1300
|
+
|
1301
|
+
includes = OpenStruct.new()
|
1302
|
+
expect(@generic_dal).to receive(:get_includes).with("query").and_return(includes)
|
1303
|
+
expect(includes).to receive(:find_index).with("name").and_return(nil)
|
1304
|
+
|
1305
|
+
expect(@generic_dal.get_relation_data_as_hash("query")).to eq({})
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
it 'should set up correct hash' do
|
1309
|
+
@generic_dal.relations = { "testname" => OpenStruct.new()}
|
1310
|
+
|
1311
|
+
includes = OpenStruct.new()
|
1312
|
+
expect(@generic_dal).to receive(:get_includes).with("testquery").and_return(includes)
|
1313
|
+
expect(includes).to receive(:find_index).with("testname").and_return(true)
|
1314
|
+
expect(@generic_dal).to receive(:get_relation_query_sql).with(includes,"testquery").and_return("testsql")
|
1315
|
+
expect(@generic_dal).to receive(:get_relation_tables).with(includes).and_return("tables")
|
1316
|
+
expect(@generic_dal).to receive(:cached_query).with("testsql", "tables").and_return("testdata")
|
1317
|
+
expect(@generic_dal).to receive(:map_to_hash_of_arrays_by_key).with("testdata", "_table_key").and_return("testreldata")
|
1318
|
+
expect(@generic_dal).to receive(:remove_key_from_hash_of_arrays!).with("testreldata", "_table_key").and_return("testreldata")
|
1319
|
+
|
1320
|
+
expect(@generic_dal.get_relation_data_as_hash("testquery")).to eq({"testname"=>"testreldata"})
|
1321
|
+
end
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
describe '#map_in_included_relations!' do
|
1325
|
+
it 'should call get_relation_data_as_hash' do
|
1326
|
+
|
1327
|
+
result = OpenStruct.new
|
1328
|
+
expect(@generic_dal).to receive(:get_relation_data_as_hash).with("testquery")
|
1329
|
+
expect(result).to receive(:each) {}
|
1330
|
+
|
1331
|
+
@generic_dal.map_in_included_relations!(result,'testquery')
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
it 'should call get_relation_data_as_hash with generic relation' do
|
1335
|
+
@generic_dal.relations = {
|
1336
|
+
"one" => { :type=>:string, :this_key => 'one' },
|
1337
|
+
}
|
1338
|
+
|
1339
|
+
dat = { "one" => "test" }
|
1340
|
+
results = [ { "one" => 1 } ]
|
1341
|
+
|
1342
|
+
expect(@generic_dal).to receive(:get_relation_data_as_hash).with("testquery").and_return(dat)
|
1343
|
+
@generic_dal.map_in_included_relations!(results,'testquery')
|
1344
|
+
|
1345
|
+
expect(results[0]).to eq({"one"=>"e"})
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
it 'should call get_relation_data_as_hash with has_one relation' do
|
1349
|
+
@generic_dal.relations = {
|
1350
|
+
"one" => { :type=>:has_one, :this_key => 'one' },
|
1351
|
+
}
|
1352
|
+
|
1353
|
+
dat = { "one" => "test" }
|
1354
|
+
results = [ { "one" => 1 } ]
|
1355
|
+
|
1356
|
+
expect(@generic_dal).to receive(:get_relation_data_as_hash).with("testquery").and_return(dat)
|
1357
|
+
@generic_dal.map_in_included_relations!(results,'testquery')
|
1358
|
+
|
1359
|
+
expect(results[0]).to eq({"one"=>"e"})
|
1360
|
+
end
|
1361
|
+
end
|
1362
|
+
|
1363
|
+
end
|