foxhound 2.0.18 → 2.0.20
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.
- package/README.md +3 -2
- package/docs/README.md +32 -11
- package/docs/_cover.md +3 -2
- package/docs/_sidebar.md +21 -1
- package/docs/_topbar.md +7 -0
- package/docs/api/README.md +73 -0
- package/docs/api/addFilter.md +170 -0
- package/docs/api/addJoin.md +128 -0
- package/docs/api/addRecord.md +108 -0
- package/docs/api/addSort.md +109 -0
- package/docs/api/behaviorFlags.md +123 -0
- package/docs/api/buildQuery.md +146 -0
- package/docs/api/clone.md +115 -0
- package/docs/api/queryOverrides.md +82 -0
- package/docs/api/setCap.md +115 -0
- package/docs/api/setDataElements.md +95 -0
- package/docs/api/setDialect.md +78 -0
- package/docs/api/setDistinct.md +76 -0
- package/docs/api/setIDUser.md +80 -0
- package/docs/api/setScope.md +70 -0
- package/docs/architecture.md +134 -42
- package/docs/dialects/README.md +2 -0
- package/docs/dialects/postgresql.md +126 -0
- package/docs/quickstart.md +196 -0
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +7996 -2630
- package/package.json +2 -2
- package/source/Foxhound-Dialects.js +4 -0
- package/source/dialects/DGraph/FoxHound-Dialect-DGraph.js +954 -0
- package/source/dialects/MongoDB/FoxHound-Dialect-MongoDB.js +902 -0
- package/source/dialects/PostgreSQL/FoxHound-Dialect-PostgreSQL.js +865 -0
- package/source/dialects/Solr/FoxHound-Dialect-Solr.js +895 -0
- package/test/FoxHound-Dialect-DGraph_tests.js +547 -0
- package/test/FoxHound-Dialect-MongoDB_tests.js +485 -0
- package/test/FoxHound-Dialect-PostgreSQL_tests.js +342 -0
- package/test/FoxHound-Dialect-Solr_tests.js +551 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for FoxHound PostgreSQL Dialect
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var Chai = require('chai');
|
|
10
|
+
var Expect = Chai.expect;
|
|
11
|
+
var Assert = Chai.assert;
|
|
12
|
+
|
|
13
|
+
var libFable = require('fable');
|
|
14
|
+
const _Fable = new libFable({Product:'FoxhoundTestsPostgreSQL'});
|
|
15
|
+
var libFoxHound = require('../source/FoxHound.js');
|
|
16
|
+
|
|
17
|
+
var _AnimalSchema = (
|
|
18
|
+
[
|
|
19
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
20
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
21
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
22
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
23
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
24
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
25
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
26
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
27
|
+
{ Column: "DeleteDate", Type:"DeleteDate" }
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
var _AnimalSchemaWithoutDeleted = (
|
|
31
|
+
[
|
|
32
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
33
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
34
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
35
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
36
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
37
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" }
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
suite
|
|
41
|
+
(
|
|
42
|
+
'FoxHound-Dialect-PostgreSQL',
|
|
43
|
+
function()
|
|
44
|
+
{
|
|
45
|
+
setup
|
|
46
|
+
(
|
|
47
|
+
function()
|
|
48
|
+
{
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
suite
|
|
53
|
+
(
|
|
54
|
+
'Object Sanity',
|
|
55
|
+
function()
|
|
56
|
+
{
|
|
57
|
+
test
|
|
58
|
+
(
|
|
59
|
+
'initialize should build a happy little object',
|
|
60
|
+
function()
|
|
61
|
+
{
|
|
62
|
+
var testFoxHound = libFoxHound.new(_Fable).setDialect('PostgreSQL');
|
|
63
|
+
Expect(testFoxHound.dialect.name)
|
|
64
|
+
.to.equal('PostgreSQL');
|
|
65
|
+
Expect(testFoxHound)
|
|
66
|
+
.to.be.an('object', 'FoxHound with PostgreSQL should initialize as an object directly from the require statement.');
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
suite
|
|
73
|
+
(
|
|
74
|
+
'Basic Query Generation',
|
|
75
|
+
function()
|
|
76
|
+
{
|
|
77
|
+
test
|
|
78
|
+
(
|
|
79
|
+
'Create Query',
|
|
80
|
+
function()
|
|
81
|
+
{
|
|
82
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
83
|
+
.setLogLevel(5)
|
|
84
|
+
.setDialect('PostgreSQL')
|
|
85
|
+
.setScope('Animal')
|
|
86
|
+
.addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
|
|
87
|
+
tmpQuery.buildCreateQuery();
|
|
88
|
+
_Fable.log.trace('Create Query', tmpQuery.query);
|
|
89
|
+
Expect(tmpQuery.query.body)
|
|
90
|
+
.to.equal('INSERT INTO "Animal" ( "IDAnimal", "Name", "Age") VALUES ( :IDAnimal_0, :Name_1, :Age_2) RETURNING *;');
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
test
|
|
94
|
+
(
|
|
95
|
+
'Create Query with Schema uses DEFAULT for AutoIdentity',
|
|
96
|
+
function()
|
|
97
|
+
{
|
|
98
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
99
|
+
.setDialect('PostgreSQL')
|
|
100
|
+
.setScope('Animal')
|
|
101
|
+
.addRecord({IDAnimal:null, GUIDAnimal:false, CreateDate:null, CreatingIDUser:null, UpdateDate:null, UpdatingIDUser:null, Deleted:0, Name:'Foo Foo', Age:15});
|
|
102
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
103
|
+
tmpQuery.query.IDUser = 37;
|
|
104
|
+
tmpQuery.query.UUID = 'test-guid-value';
|
|
105
|
+
tmpQuery.buildCreateQuery();
|
|
106
|
+
_Fable.log.trace('Create Query with Schema', tmpQuery.query);
|
|
107
|
+
// AutoIdentity should use DEFAULT (not NULL like MySQL)
|
|
108
|
+
Expect(tmpQuery.query.body).to.contain('DEFAULT');
|
|
109
|
+
Expect(tmpQuery.query.body).to.contain('RETURNING *');
|
|
110
|
+
Expect(tmpQuery.query.body).to.contain('NOW()');
|
|
111
|
+
Expect(tmpQuery.query.body).to.not.contain('NOW(3)');
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
test
|
|
115
|
+
(
|
|
116
|
+
'Bad Create Query',
|
|
117
|
+
function()
|
|
118
|
+
{
|
|
119
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('PostgreSQL');
|
|
120
|
+
tmpQuery.buildCreateQuery();
|
|
121
|
+
tmpQuery.addRecord({});
|
|
122
|
+
tmpQuery.buildCreateQuery();
|
|
123
|
+
_Fable.log.trace('Create Query', tmpQuery.query);
|
|
124
|
+
Expect(tmpQuery.query.body)
|
|
125
|
+
.to.equal(false);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
test
|
|
129
|
+
(
|
|
130
|
+
'Read Query',
|
|
131
|
+
function()
|
|
132
|
+
{
|
|
133
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('PostgreSQL').setScope('Animal');
|
|
134
|
+
tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
|
|
135
|
+
tmpQuery.buildReadQuery();
|
|
136
|
+
_Fable.log.trace('Simple Select Query', tmpQuery.query);
|
|
137
|
+
Expect(tmpQuery.query.body)
|
|
138
|
+
.to.equal('SELECT "Animal".* FROM "Animal" ORDER BY "Cost" DESC;');
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
test
|
|
142
|
+
(
|
|
143
|
+
'Read Query with Distinct',
|
|
144
|
+
function()
|
|
145
|
+
{
|
|
146
|
+
var tmpQuery = libFoxHound.new(_Fable).setDialect('PostgreSQL').setScope('Animal');
|
|
147
|
+
tmpQuery.addSort({Column:'Cost',Direction:'Descending'})
|
|
148
|
+
.setDistinct(true);
|
|
149
|
+
tmpQuery.buildReadQuery();
|
|
150
|
+
_Fable.log.trace('Simple Select Query', tmpQuery.query);
|
|
151
|
+
Expect(tmpQuery.query.body)
|
|
152
|
+
.to.equal('SELECT DISTINCT "Animal".* FROM "Animal" ORDER BY "Cost" DESC;');
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
test
|
|
156
|
+
(
|
|
157
|
+
'Complex Read Query uses LIMIT/OFFSET syntax',
|
|
158
|
+
function()
|
|
159
|
+
{
|
|
160
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
161
|
+
.setDialect('PostgreSQL')
|
|
162
|
+
.setScope('Animal')
|
|
163
|
+
.setCap(10)
|
|
164
|
+
.setBegin(0)
|
|
165
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
166
|
+
.setSort([{Column:'Age',Direction:'Ascending'}])
|
|
167
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
|
|
168
|
+
tmpQuery.addSort('Cost');
|
|
169
|
+
tmpQuery.buildReadQuery();
|
|
170
|
+
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
171
|
+
Expect(tmpQuery.query.body)
|
|
172
|
+
.to.equal('SELECT "Name", "Age", "Cost" FROM "Animal" WHERE "Age" = :Age_w0 ORDER BY "Age", "Cost" LIMIT 10 OFFSET 0;');
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
test
|
|
176
|
+
(
|
|
177
|
+
'Complex Read Query with qualified and unqualified "SELECT *" cases',
|
|
178
|
+
function()
|
|
179
|
+
{
|
|
180
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
181
|
+
.setDialect('PostgreSQL')
|
|
182
|
+
.setScope('Animal')
|
|
183
|
+
.setCap(10)
|
|
184
|
+
.setBegin(0)
|
|
185
|
+
.setDataElements(['*', 'Name', 'Age', 'Cost', 'Animal.*'])
|
|
186
|
+
.setSort([{Column:'Age',Direction:'Ascending'}])
|
|
187
|
+
.setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
|
|
188
|
+
tmpQuery.addSort('Cost');
|
|
189
|
+
tmpQuery.buildReadQuery();
|
|
190
|
+
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
191
|
+
Expect(tmpQuery.query.body)
|
|
192
|
+
.to.equal('SELECT *, "Name", "Age", "Cost", "Animal".* FROM "Animal" WHERE "Age" = :Age_w0 ORDER BY "Age", "Cost" LIMIT 10 OFFSET 0;');
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
test
|
|
196
|
+
(
|
|
197
|
+
'Complex Read Query with Filters',
|
|
198
|
+
function()
|
|
199
|
+
{
|
|
200
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
201
|
+
.setDialect('PostgreSQL')
|
|
202
|
+
.setScope('Animal')
|
|
203
|
+
.setDataElements(['Name', 'Age', 'Cost'])
|
|
204
|
+
.setCap(100)
|
|
205
|
+
.addFilter('Age', '25')
|
|
206
|
+
.addFilter('', '', '(')
|
|
207
|
+
.addFilter('Color', 'Red')
|
|
208
|
+
.addFilter('Color', 'Green', '=', 'OR')
|
|
209
|
+
.addFilter('', '', ')')
|
|
210
|
+
.addFilter('Description', '', 'IS NOT NULL')
|
|
211
|
+
.addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN');
|
|
212
|
+
tmpQuery.setLogLevel(3).addSort('Age');
|
|
213
|
+
tmpQuery.buildReadQuery();
|
|
214
|
+
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
215
|
+
Expect(tmpQuery.query.body)
|
|
216
|
+
.to.equal('SELECT "Name", "Age", "Cost" FROM "Animal" WHERE "Age" = :Age_w0 AND ( "Color" = :Color_w2 OR "Color" = :Color_w3 ) AND "Description" IS NOT NULL AND "IDOffice" IN ( :IDOffice_w6 ) ORDER BY "Age" LIMIT 100;');
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
test
|
|
220
|
+
(
|
|
221
|
+
'Update Query',
|
|
222
|
+
function()
|
|
223
|
+
{
|
|
224
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
225
|
+
.setDialect('PostgreSQL')
|
|
226
|
+
.setScope('Animal')
|
|
227
|
+
.addFilter('IDAnimal', 9)
|
|
228
|
+
.addRecord({Name:'Froggy', Age:12});
|
|
229
|
+
tmpQuery.buildUpdateQuery();
|
|
230
|
+
_Fable.log.trace('Update Query', tmpQuery.query);
|
|
231
|
+
Expect(tmpQuery.query.body)
|
|
232
|
+
.to.equal('UPDATE "Animal" SET "Name" = :Name_0, "Age" = :Age_1 WHERE "IDAnimal" = :IDAnimal_w0;');
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
test
|
|
236
|
+
(
|
|
237
|
+
'Count Query',
|
|
238
|
+
function()
|
|
239
|
+
{
|
|
240
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
241
|
+
.setDialect('PostgreSQL')
|
|
242
|
+
.setScope('Animal')
|
|
243
|
+
.addFilter('Age', '3');
|
|
244
|
+
tmpQuery.buildCountQuery();
|
|
245
|
+
_Fable.log.trace('Count Query', tmpQuery.query);
|
|
246
|
+
Expect(tmpQuery.query.body)
|
|
247
|
+
.to.equal('SELECT COUNT(*) AS RowCount FROM "Animal" WHERE "Age" = :Age_w0;');
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
test
|
|
251
|
+
(
|
|
252
|
+
'Delete Query with soft delete schema',
|
|
253
|
+
function()
|
|
254
|
+
{
|
|
255
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
256
|
+
.setDialect('PostgreSQL')
|
|
257
|
+
.setScope('Animal')
|
|
258
|
+
.addFilter('IDAnimal', 9);
|
|
259
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
260
|
+
tmpQuery.query.IDUser = 37;
|
|
261
|
+
tmpQuery.buildDeleteQuery();
|
|
262
|
+
_Fable.log.trace('Delete Query', tmpQuery.query);
|
|
263
|
+
Expect(tmpQuery.query.body)
|
|
264
|
+
.to.contain('UPDATE "Animal" SET');
|
|
265
|
+
Expect(tmpQuery.query.body)
|
|
266
|
+
.to.contain('"Deleted" = 1');
|
|
267
|
+
Expect(tmpQuery.query.body)
|
|
268
|
+
.to.contain('NOW()');
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
test
|
|
272
|
+
(
|
|
273
|
+
'Delete Query without schema does hard delete',
|
|
274
|
+
function()
|
|
275
|
+
{
|
|
276
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
277
|
+
.setDialect('PostgreSQL')
|
|
278
|
+
.setScope('Animal')
|
|
279
|
+
.addFilter('IDAnimal', 9);
|
|
280
|
+
tmpQuery.buildDeleteQuery();
|
|
281
|
+
_Fable.log.trace('Delete Query', tmpQuery.query);
|
|
282
|
+
Expect(tmpQuery.query.body)
|
|
283
|
+
.to.equal('DELETE FROM "Animal" WHERE "IDAnimal" = :IDAnimal_w0;');
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
test
|
|
287
|
+
(
|
|
288
|
+
'Undelete Query',
|
|
289
|
+
function()
|
|
290
|
+
{
|
|
291
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
292
|
+
.setDialect('PostgreSQL')
|
|
293
|
+
.setScope('Animal')
|
|
294
|
+
.addFilter('IDAnimal', 9);
|
|
295
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
296
|
+
tmpQuery.query.IDUser = 37;
|
|
297
|
+
tmpQuery.buildUndeleteQuery();
|
|
298
|
+
_Fable.log.trace('Undelete Query', tmpQuery.query);
|
|
299
|
+
Expect(tmpQuery.query.body)
|
|
300
|
+
.to.contain('UPDATE "Animal" SET');
|
|
301
|
+
Expect(tmpQuery.query.body)
|
|
302
|
+
.to.contain('"Deleted" = 0');
|
|
303
|
+
Expect(tmpQuery.query.body)
|
|
304
|
+
.to.contain('NOW()');
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
test
|
|
308
|
+
(
|
|
309
|
+
'Read Query without LIMIT has no OFFSET',
|
|
310
|
+
function()
|
|
311
|
+
{
|
|
312
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
313
|
+
.setDialect('PostgreSQL')
|
|
314
|
+
.setScope('Animal');
|
|
315
|
+
tmpQuery.buildReadQuery();
|
|
316
|
+
_Fable.log.trace('Select Query', tmpQuery.query);
|
|
317
|
+
Expect(tmpQuery.query.body)
|
|
318
|
+
.to.equal('SELECT "Animal".* FROM "Animal";');
|
|
319
|
+
Expect(tmpQuery.query.body).to.not.contain('LIMIT');
|
|
320
|
+
Expect(tmpQuery.query.body).to.not.contain('OFFSET');
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
test
|
|
324
|
+
(
|
|
325
|
+
'Read Query uses double-quote identifiers (not backticks)',
|
|
326
|
+
function()
|
|
327
|
+
{
|
|
328
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
329
|
+
.setDialect('PostgreSQL')
|
|
330
|
+
.setScope('Animal')
|
|
331
|
+
.setDataElements(['Name', 'Age']);
|
|
332
|
+
tmpQuery.buildReadQuery();
|
|
333
|
+
Expect(tmpQuery.query.body).to.not.contain('`');
|
|
334
|
+
Expect(tmpQuery.query.body).to.contain('"Name"');
|
|
335
|
+
Expect(tmpQuery.query.body).to.contain('"Age"');
|
|
336
|
+
Expect(tmpQuery.query.body).to.contain('"Animal"');
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
);
|