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.
Files changed (36) hide show
  1. package/README.md +3 -2
  2. package/docs/README.md +32 -11
  3. package/docs/_cover.md +3 -2
  4. package/docs/_sidebar.md +21 -1
  5. package/docs/_topbar.md +7 -0
  6. package/docs/api/README.md +73 -0
  7. package/docs/api/addFilter.md +170 -0
  8. package/docs/api/addJoin.md +128 -0
  9. package/docs/api/addRecord.md +108 -0
  10. package/docs/api/addSort.md +109 -0
  11. package/docs/api/behaviorFlags.md +123 -0
  12. package/docs/api/buildQuery.md +146 -0
  13. package/docs/api/clone.md +115 -0
  14. package/docs/api/queryOverrides.md +82 -0
  15. package/docs/api/setCap.md +115 -0
  16. package/docs/api/setDataElements.md +95 -0
  17. package/docs/api/setDialect.md +78 -0
  18. package/docs/api/setDistinct.md +76 -0
  19. package/docs/api/setIDUser.md +80 -0
  20. package/docs/api/setScope.md +70 -0
  21. package/docs/architecture.md +134 -42
  22. package/docs/dialects/README.md +2 -0
  23. package/docs/dialects/postgresql.md +126 -0
  24. package/docs/quickstart.md +196 -0
  25. package/docs/retold-catalog.json +40 -1
  26. package/docs/retold-keyword-index.json +7996 -2630
  27. package/package.json +2 -2
  28. package/source/Foxhound-Dialects.js +4 -0
  29. package/source/dialects/DGraph/FoxHound-Dialect-DGraph.js +954 -0
  30. package/source/dialects/MongoDB/FoxHound-Dialect-MongoDB.js +902 -0
  31. package/source/dialects/PostgreSQL/FoxHound-Dialect-PostgreSQL.js +865 -0
  32. package/source/dialects/Solr/FoxHound-Dialect-Solr.js +895 -0
  33. package/test/FoxHound-Dialect-DGraph_tests.js +547 -0
  34. package/test/FoxHound-Dialect-MongoDB_tests.js +485 -0
  35. package/test/FoxHound-Dialect-PostgreSQL_tests.js +342 -0
  36. 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
+ );