meadow 2.0.22 → 2.0.26
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 +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -27
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-Solr_tests.js +679 -0
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the Meadow "RocksDB" Provider
|
|
3
|
+
*
|
|
4
|
+
* These tests use a local RocksDB database on disk.
|
|
5
|
+
*
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
var Chai = require("chai");
|
|
12
|
+
var Expect = Chai.expect;
|
|
13
|
+
|
|
14
|
+
var libFS = require('fs');
|
|
15
|
+
var libPath = require('path');
|
|
16
|
+
|
|
17
|
+
var libMeadowConnectionRocksDB = require('meadow-connection-rocksdb');
|
|
18
|
+
|
|
19
|
+
var _RocksDBFolder = libPath.join(__dirname, '..', 'dist', 'FableTest-RocksDB');
|
|
20
|
+
|
|
21
|
+
var tmpFableSettings = (
|
|
22
|
+
{
|
|
23
|
+
RocksDB:
|
|
24
|
+
{
|
|
25
|
+
RocksDBFolder: _RocksDBFolder
|
|
26
|
+
},
|
|
27
|
+
LogStreams:
|
|
28
|
+
[
|
|
29
|
+
{
|
|
30
|
+
level: 'fatal',
|
|
31
|
+
streamtype:'process.stdout',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
level: 'trace',
|
|
35
|
+
path: __dirname+'/../tests.log'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
var libFable = new (require('fable'))(tmpFableSettings);
|
|
41
|
+
|
|
42
|
+
// Register the RocksDB connection service
|
|
43
|
+
libFable.serviceManager.addServiceType('MeadowRocksDBProvider', libMeadowConnectionRocksDB);
|
|
44
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowRocksDBProvider');
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
var _AnimalJsonSchema = (
|
|
48
|
+
{
|
|
49
|
+
title: "Animal",
|
|
50
|
+
description: "A creature that lives in a meadow.",
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
IDAnimal: {
|
|
54
|
+
description: "The unique identifier for an animal",
|
|
55
|
+
type: "integer"
|
|
56
|
+
},
|
|
57
|
+
Name: {
|
|
58
|
+
description: "The animal's name",
|
|
59
|
+
type: "string"
|
|
60
|
+
},
|
|
61
|
+
Type: {
|
|
62
|
+
description: "The type of the animal",
|
|
63
|
+
type: "string"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
required: ["IDAnimal", "Name", "CreatingIDUser"]
|
|
67
|
+
});
|
|
68
|
+
var _AnimalSchema = (
|
|
69
|
+
[
|
|
70
|
+
{ Column: "IDAnimal", Type:"AutoIdentity" },
|
|
71
|
+
{ Column: "GUIDAnimal", Type:"AutoGUID" },
|
|
72
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
73
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
74
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
75
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
76
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
77
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
78
|
+
{ Column: "DeleteDate", Type:"DeleteDate" }
|
|
79
|
+
]);
|
|
80
|
+
var _AnimalDefault = (
|
|
81
|
+
{
|
|
82
|
+
IDAnimal: null,
|
|
83
|
+
GUIDAnimal: '',
|
|
84
|
+
|
|
85
|
+
CreateDate: false,
|
|
86
|
+
CreatingIDUser: 0,
|
|
87
|
+
UpdateDate: false,
|
|
88
|
+
UpdatingIDUser: 0,
|
|
89
|
+
Deleted: 0,
|
|
90
|
+
DeleteDate: false,
|
|
91
|
+
DeletingIDUser: 0,
|
|
92
|
+
|
|
93
|
+
Name: 'Unknown',
|
|
94
|
+
Type: 'Unclassified'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
suite
|
|
98
|
+
(
|
|
99
|
+
'Meadow-Provider-RocksDB',
|
|
100
|
+
function()
|
|
101
|
+
{
|
|
102
|
+
var _SpooledUp = false;
|
|
103
|
+
|
|
104
|
+
var newMeadow = function()
|
|
105
|
+
{
|
|
106
|
+
return require('../source/Meadow.js')
|
|
107
|
+
.new(libFable, 'FableTest')
|
|
108
|
+
.setProvider('RocksDB')
|
|
109
|
+
.setSchema(_AnimalSchema)
|
|
110
|
+
.setJsonSchema(_AnimalJsonSchema)
|
|
111
|
+
.setDefaultIdentifier('IDAnimal')
|
|
112
|
+
.setDefault(_AnimalDefault);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
suiteSetup
|
|
116
|
+
(
|
|
117
|
+
function(fDone)
|
|
118
|
+
{
|
|
119
|
+
if (!_SpooledUp)
|
|
120
|
+
{
|
|
121
|
+
// Remove any previous test database
|
|
122
|
+
if (libFS.existsSync(_RocksDBFolder))
|
|
123
|
+
{
|
|
124
|
+
libFS.rmSync(_RocksDBFolder, { recursive: true, force: true });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Ensure the dist directory exists
|
|
128
|
+
var tmpDistDir = libPath.join(__dirname, '..', 'dist');
|
|
129
|
+
if (!libFS.existsSync(tmpDistDir))
|
|
130
|
+
{
|
|
131
|
+
libFS.mkdirSync(tmpDistDir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Connect to RocksDB
|
|
135
|
+
libFable.MeadowRocksDBProvider.connectAsync(
|
|
136
|
+
function(pError)
|
|
137
|
+
{
|
|
138
|
+
if (pError)
|
|
139
|
+
{
|
|
140
|
+
return fDone(pError);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_SpooledUp = true;
|
|
144
|
+
fDone();
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
{
|
|
150
|
+
fDone();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
suiteTeardown(function(fDone)
|
|
156
|
+
{
|
|
157
|
+
libFable.MeadowRocksDBProvider.closeAsync(function()
|
|
158
|
+
{
|
|
159
|
+
fDone();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
suite
|
|
164
|
+
(
|
|
165
|
+
'Object Sanity',
|
|
166
|
+
function()
|
|
167
|
+
{
|
|
168
|
+
test
|
|
169
|
+
(
|
|
170
|
+
'The RocksDB class should initialize itself into a happy little object.',
|
|
171
|
+
function()
|
|
172
|
+
{
|
|
173
|
+
var testMeadow = require('../source/Meadow.js').new(libFable).setProvider('RocksDB');
|
|
174
|
+
Expect(testMeadow).to.be.an('object', 'Meadow should initialize as an object directly from the require statement.');
|
|
175
|
+
Expect(testMeadow.providerName).to.equal('RocksDB');
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
suite
|
|
182
|
+
(
|
|
183
|
+
'Create Operations',
|
|
184
|
+
function()
|
|
185
|
+
{
|
|
186
|
+
test
|
|
187
|
+
(
|
|
188
|
+
'Create a record in the database',
|
|
189
|
+
function(fDone)
|
|
190
|
+
{
|
|
191
|
+
var testMeadow = newMeadow().setIDUser(90210);
|
|
192
|
+
|
|
193
|
+
var tmpQuery = testMeadow.query.clone().setLogLevel(5)
|
|
194
|
+
.addRecord({Name:'Foo Foo', Type:'Bunny'});
|
|
195
|
+
|
|
196
|
+
testMeadow.doCreate(tmpQuery,
|
|
197
|
+
function(pError, pQuery, pQueryRead, pRecord)
|
|
198
|
+
{
|
|
199
|
+
Expect(pRecord.Name)
|
|
200
|
+
.to.equal('Foo Foo');
|
|
201
|
+
Expect(pRecord.Type)
|
|
202
|
+
.to.equal('Bunny');
|
|
203
|
+
Expect(pRecord.IDAnimal)
|
|
204
|
+
.to.equal(1);
|
|
205
|
+
Expect(pRecord.CreatingIDUser)
|
|
206
|
+
.to.equal(90210);
|
|
207
|
+
Expect(pRecord.GUIDAnimal)
|
|
208
|
+
.to.be.a('string');
|
|
209
|
+
Expect(pRecord.GUIDAnimal.length)
|
|
210
|
+
.to.be.greaterThan(5);
|
|
211
|
+
fDone();
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
test
|
|
218
|
+
(
|
|
219
|
+
'Create more records for later tests',
|
|
220
|
+
function(fDone)
|
|
221
|
+
{
|
|
222
|
+
var testMeadow = newMeadow().setIDUser(1);
|
|
223
|
+
|
|
224
|
+
var tmpQuery1 = testMeadow.query.clone()
|
|
225
|
+
.addRecord({Name:'Red Riding Hood', Type:'Human'});
|
|
226
|
+
testMeadow.doCreate(tmpQuery1,
|
|
227
|
+
function(pError1, pQuery1, pQueryRead1, pRecord1)
|
|
228
|
+
{
|
|
229
|
+
Expect(pRecord1.IDAnimal).to.equal(2);
|
|
230
|
+
Expect(pRecord1.Name).to.equal('Red Riding Hood');
|
|
231
|
+
|
|
232
|
+
var tmpQuery2 = testMeadow.query.clone()
|
|
233
|
+
.addRecord({Name:'Red', Type:'Dog'});
|
|
234
|
+
testMeadow.doCreate(tmpQuery2,
|
|
235
|
+
function(pError2, pQuery2, pQueryRead2, pRecord2)
|
|
236
|
+
{
|
|
237
|
+
Expect(pRecord2.IDAnimal).to.equal(3);
|
|
238
|
+
|
|
239
|
+
var tmpQuery3 = testMeadow.query.clone()
|
|
240
|
+
.addRecord({Name:'Spot', Type:'Dog'});
|
|
241
|
+
testMeadow.doCreate(tmpQuery3,
|
|
242
|
+
function(pError3, pQuery3, pQueryRead3, pRecord3)
|
|
243
|
+
{
|
|
244
|
+
Expect(pRecord3.IDAnimal).to.equal(4);
|
|
245
|
+
|
|
246
|
+
var tmpQuery4 = testMeadow.query.clone()
|
|
247
|
+
.addRecord({Name:'Gertrude', Type:'Frog'});
|
|
248
|
+
testMeadow.doCreate(tmpQuery4,
|
|
249
|
+
function(pError4, pQuery4, pQueryRead4, pRecord4)
|
|
250
|
+
{
|
|
251
|
+
Expect(pRecord4.IDAnimal).to.equal(5);
|
|
252
|
+
fDone();
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
test
|
|
265
|
+
(
|
|
266
|
+
'Create a record with pre-set Deleted flag',
|
|
267
|
+
function(fDone)
|
|
268
|
+
{
|
|
269
|
+
var testMeadow = newMeadow().setIDUser(1);
|
|
270
|
+
|
|
271
|
+
var tmpQuery = testMeadow.query.clone()
|
|
272
|
+
.setDisableDeleteTracking(true)
|
|
273
|
+
.addRecord({Name:'Charmander', Type:'Pokemon', Deleted: 1});
|
|
274
|
+
|
|
275
|
+
testMeadow.doCreate(tmpQuery,
|
|
276
|
+
function(pError, pQuery, pQueryRead, pRecord)
|
|
277
|
+
{
|
|
278
|
+
Expect(pRecord.Name).to.equal('Charmander');
|
|
279
|
+
Expect(pRecord.Deleted).to.equal(1);
|
|
280
|
+
Expect(pRecord.IDAnimal).to.equal(6);
|
|
281
|
+
fDone();
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
suite
|
|
290
|
+
(
|
|
291
|
+
'Read Operations',
|
|
292
|
+
function()
|
|
293
|
+
{
|
|
294
|
+
test
|
|
295
|
+
(
|
|
296
|
+
'Read a single record by ID',
|
|
297
|
+
function(fDone)
|
|
298
|
+
{
|
|
299
|
+
var testMeadow = newMeadow();
|
|
300
|
+
|
|
301
|
+
var tmpQuery = testMeadow.query
|
|
302
|
+
.addFilter('IDAnimal', 1);
|
|
303
|
+
|
|
304
|
+
testMeadow.doRead(tmpQuery,
|
|
305
|
+
function(pError, pQuery, pRecord)
|
|
306
|
+
{
|
|
307
|
+
Expect(pRecord.IDAnimal)
|
|
308
|
+
.to.equal(1);
|
|
309
|
+
Expect(pRecord.Name)
|
|
310
|
+
.to.equal('Foo Foo');
|
|
311
|
+
Expect(pRecord.Type)
|
|
312
|
+
.to.equal('Bunny');
|
|
313
|
+
fDone();
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
test
|
|
320
|
+
(
|
|
321
|
+
'Read all records (doReads)',
|
|
322
|
+
function(fDone)
|
|
323
|
+
{
|
|
324
|
+
var testMeadow = newMeadow();
|
|
325
|
+
|
|
326
|
+
testMeadow.doReads(testMeadow.query,
|
|
327
|
+
function(pError, pQuery, pRecords)
|
|
328
|
+
{
|
|
329
|
+
// Should return 5 records (excluding Charmander which is Deleted=1)
|
|
330
|
+
Expect(pRecords.length)
|
|
331
|
+
.to.equal(5);
|
|
332
|
+
// Records come back in GUID key order, so use set-based assertions
|
|
333
|
+
var tmpNames = pRecords.map(function(r) { return r.Name; });
|
|
334
|
+
Expect(tmpNames).to.include('Foo Foo');
|
|
335
|
+
Expect(tmpNames).to.include('Red Riding Hood');
|
|
336
|
+
Expect(tmpNames).to.include('Red');
|
|
337
|
+
Expect(tmpNames).to.include('Spot');
|
|
338
|
+
Expect(tmpNames).to.include('Gertrude');
|
|
339
|
+
fDone();
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
test
|
|
346
|
+
(
|
|
347
|
+
'Read records with equality filter',
|
|
348
|
+
function(fDone)
|
|
349
|
+
{
|
|
350
|
+
var testMeadow = newMeadow();
|
|
351
|
+
|
|
352
|
+
var tmpQuery = testMeadow.query
|
|
353
|
+
.addFilter('Type', 'Dog');
|
|
354
|
+
|
|
355
|
+
testMeadow.doReads(tmpQuery,
|
|
356
|
+
function(pError, pQuery, pRecords)
|
|
357
|
+
{
|
|
358
|
+
Expect(pRecords.length)
|
|
359
|
+
.to.equal(2);
|
|
360
|
+
// Records come back in GUID key order, so use set-based assertions
|
|
361
|
+
var tmpNames = pRecords.map(function(r) { return r.Name; });
|
|
362
|
+
Expect(tmpNames).to.include('Red');
|
|
363
|
+
Expect(tmpNames).to.include('Spot');
|
|
364
|
+
fDone();
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
test
|
|
371
|
+
(
|
|
372
|
+
'Read records with LIKE filter',
|
|
373
|
+
function(fDone)
|
|
374
|
+
{
|
|
375
|
+
var testMeadow = newMeadow();
|
|
376
|
+
|
|
377
|
+
var tmpQuery = testMeadow.query
|
|
378
|
+
.addFilter('Name', '%Red%', 'LIKE');
|
|
379
|
+
|
|
380
|
+
testMeadow.doReads(tmpQuery,
|
|
381
|
+
function(pError, pQuery, pRecords)
|
|
382
|
+
{
|
|
383
|
+
Expect(pRecords.length)
|
|
384
|
+
.to.equal(2);
|
|
385
|
+
// Should match "Red Riding Hood" and "Red"
|
|
386
|
+
var tmpNames = pRecords.map(function(r) { return r.Name; });
|
|
387
|
+
Expect(tmpNames).to.include('Red Riding Hood');
|
|
388
|
+
Expect(tmpNames).to.include('Red');
|
|
389
|
+
fDone();
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
test
|
|
396
|
+
(
|
|
397
|
+
'Read records with IN filter',
|
|
398
|
+
function(fDone)
|
|
399
|
+
{
|
|
400
|
+
var testMeadow = newMeadow();
|
|
401
|
+
|
|
402
|
+
var tmpQuery = testMeadow.query
|
|
403
|
+
.addFilter('Type', ['Bunny', 'Frog'], 'IN');
|
|
404
|
+
|
|
405
|
+
testMeadow.doReads(tmpQuery,
|
|
406
|
+
function(pError, pQuery, pRecords)
|
|
407
|
+
{
|
|
408
|
+
Expect(pRecords.length)
|
|
409
|
+
.to.equal(2);
|
|
410
|
+
var tmpTypes = pRecords.map(function(r) { return r.Type; });
|
|
411
|
+
Expect(tmpTypes).to.include('Bunny');
|
|
412
|
+
Expect(tmpTypes).to.include('Frog');
|
|
413
|
+
fDone();
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
test
|
|
420
|
+
(
|
|
421
|
+
'Read records with comparison filter (>)',
|
|
422
|
+
function(fDone)
|
|
423
|
+
{
|
|
424
|
+
var testMeadow = newMeadow();
|
|
425
|
+
|
|
426
|
+
var tmpQuery = testMeadow.query
|
|
427
|
+
.addFilter('IDAnimal', 3, '>');
|
|
428
|
+
|
|
429
|
+
testMeadow.doReads(tmpQuery,
|
|
430
|
+
function(pError, pQuery, pRecords)
|
|
431
|
+
{
|
|
432
|
+
Expect(pRecords.length)
|
|
433
|
+
.to.equal(2);
|
|
434
|
+
// IDs 4 and 5 (not 6 since it's Deleted); order depends on GUID keys
|
|
435
|
+
var tmpIDs = pRecords.map(function(r) { return r.IDAnimal; });
|
|
436
|
+
Expect(tmpIDs).to.include(4);
|
|
437
|
+
Expect(tmpIDs).to.include(5);
|
|
438
|
+
fDone();
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
test
|
|
445
|
+
(
|
|
446
|
+
'Read records with NOT IN filter',
|
|
447
|
+
function(fDone)
|
|
448
|
+
{
|
|
449
|
+
var testMeadow = newMeadow();
|
|
450
|
+
|
|
451
|
+
var tmpQuery = testMeadow.query
|
|
452
|
+
.addFilter('Type', ['Dog', 'Human', 'Frog'], 'NOT IN');
|
|
453
|
+
|
|
454
|
+
testMeadow.doReads(tmpQuery,
|
|
455
|
+
function(pError, pQuery, pRecords)
|
|
456
|
+
{
|
|
457
|
+
// Should only be the Bunny
|
|
458
|
+
Expect(pRecords.length)
|
|
459
|
+
.to.equal(1);
|
|
460
|
+
Expect(pRecords[0].Type)
|
|
461
|
+
.to.equal('Bunny');
|
|
462
|
+
fDone();
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
test
|
|
469
|
+
(
|
|
470
|
+
'Read records with sorting',
|
|
471
|
+
function(fDone)
|
|
472
|
+
{
|
|
473
|
+
var testMeadow = newMeadow();
|
|
474
|
+
|
|
475
|
+
var tmpQuery = testMeadow.query
|
|
476
|
+
.addSort({Column:'Name', Direction:'Descending'});
|
|
477
|
+
|
|
478
|
+
testMeadow.doReads(tmpQuery,
|
|
479
|
+
function(pError, pQuery, pRecords)
|
|
480
|
+
{
|
|
481
|
+
Expect(pRecords.length)
|
|
482
|
+
.to.equal(5);
|
|
483
|
+
// Alphabetically descending
|
|
484
|
+
Expect(pRecords[0].Name)
|
|
485
|
+
.to.equal('Spot');
|
|
486
|
+
Expect(pRecords[pRecords.length - 1].Name)
|
|
487
|
+
.to.equal('Foo Foo');
|
|
488
|
+
fDone();
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
test
|
|
495
|
+
(
|
|
496
|
+
'Read records with pagination (skip and limit)',
|
|
497
|
+
function(fDone)
|
|
498
|
+
{
|
|
499
|
+
var testMeadow = newMeadow();
|
|
500
|
+
|
|
501
|
+
var tmpQuery = testMeadow.query
|
|
502
|
+
.addSort({Column:'IDAnimal', Direction:'Ascending'})
|
|
503
|
+
.setCap(2)
|
|
504
|
+
.setBegin(1);
|
|
505
|
+
|
|
506
|
+
testMeadow.doReads(tmpQuery,
|
|
507
|
+
function(pError, pQuery, pRecords)
|
|
508
|
+
{
|
|
509
|
+
// Should skip 1 and return 2 (sorted by IDAnimal ascending)
|
|
510
|
+
Expect(pRecords.length)
|
|
511
|
+
.to.equal(2);
|
|
512
|
+
Expect(pRecords[0].IDAnimal)
|
|
513
|
+
.to.equal(2);
|
|
514
|
+
Expect(pRecords[1].IDAnimal)
|
|
515
|
+
.to.equal(3);
|
|
516
|
+
fDone();
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
suite
|
|
525
|
+
(
|
|
526
|
+
'Update Operations',
|
|
527
|
+
function()
|
|
528
|
+
{
|
|
529
|
+
test
|
|
530
|
+
(
|
|
531
|
+
'Update a record in the database',
|
|
532
|
+
function(fDone)
|
|
533
|
+
{
|
|
534
|
+
var testMeadow = newMeadow().setIDUser(42);
|
|
535
|
+
|
|
536
|
+
var tmpQuery = testMeadow.query
|
|
537
|
+
.addRecord({IDAnimal:2, Type:'Girl'});
|
|
538
|
+
|
|
539
|
+
testMeadow.doUpdate(tmpQuery,
|
|
540
|
+
function(pError, pQuery, pQueryRead, pRecord)
|
|
541
|
+
{
|
|
542
|
+
Expect(pRecord.Type)
|
|
543
|
+
.to.equal('Girl');
|
|
544
|
+
Expect(pRecord.Name)
|
|
545
|
+
.to.equal('Red Riding Hood');
|
|
546
|
+
Expect(pRecord.UpdatingIDUser)
|
|
547
|
+
.to.equal(42);
|
|
548
|
+
fDone();
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
test
|
|
555
|
+
(
|
|
556
|
+
'Verify update persisted',
|
|
557
|
+
function(fDone)
|
|
558
|
+
{
|
|
559
|
+
var testMeadow = newMeadow();
|
|
560
|
+
|
|
561
|
+
var tmpQuery = testMeadow.query
|
|
562
|
+
.addFilter('IDAnimal', 2);
|
|
563
|
+
|
|
564
|
+
testMeadow.doRead(tmpQuery,
|
|
565
|
+
function(pError, pQuery, pRecord)
|
|
566
|
+
{
|
|
567
|
+
Expect(pRecord.Type)
|
|
568
|
+
.to.equal('Girl');
|
|
569
|
+
fDone();
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
suite
|
|
578
|
+
(
|
|
579
|
+
'Count Operations',
|
|
580
|
+
function()
|
|
581
|
+
{
|
|
582
|
+
test
|
|
583
|
+
(
|
|
584
|
+
'Count all non-deleted records',
|
|
585
|
+
function(fDone)
|
|
586
|
+
{
|
|
587
|
+
var testMeadow = newMeadow();
|
|
588
|
+
|
|
589
|
+
testMeadow.doCount(testMeadow.query,
|
|
590
|
+
function(pError, pQuery, pCount)
|
|
591
|
+
{
|
|
592
|
+
Expect(pCount)
|
|
593
|
+
.to.equal(5);
|
|
594
|
+
fDone();
|
|
595
|
+
}
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
test
|
|
601
|
+
(
|
|
602
|
+
'Count records with filter',
|
|
603
|
+
function(fDone)
|
|
604
|
+
{
|
|
605
|
+
var testMeadow = newMeadow();
|
|
606
|
+
|
|
607
|
+
var tmpQuery = testMeadow.query
|
|
608
|
+
.addFilter('Type', 'Dog');
|
|
609
|
+
|
|
610
|
+
testMeadow.doCount(tmpQuery,
|
|
611
|
+
function(pError, pQuery, pCount)
|
|
612
|
+
{
|
|
613
|
+
Expect(pCount)
|
|
614
|
+
.to.equal(2);
|
|
615
|
+
fDone();
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
suite
|
|
624
|
+
(
|
|
625
|
+
'Delete Operations',
|
|
626
|
+
function()
|
|
627
|
+
{
|
|
628
|
+
test
|
|
629
|
+
(
|
|
630
|
+
'Soft-delete a record',
|
|
631
|
+
function(fDone)
|
|
632
|
+
{
|
|
633
|
+
var testMeadow = newMeadow().setIDUser(999);
|
|
634
|
+
|
|
635
|
+
var tmpQuery = testMeadow.query
|
|
636
|
+
.addFilter('IDAnimal', 5);
|
|
637
|
+
|
|
638
|
+
testMeadow.doDelete(tmpQuery,
|
|
639
|
+
function(pError, pQuery, pCount)
|
|
640
|
+
{
|
|
641
|
+
Expect(pCount)
|
|
642
|
+
.to.equal(1);
|
|
643
|
+
fDone();
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
test
|
|
650
|
+
(
|
|
651
|
+
'Verify soft-deleted record is excluded from reads',
|
|
652
|
+
function(fDone)
|
|
653
|
+
{
|
|
654
|
+
var testMeadow = newMeadow();
|
|
655
|
+
|
|
656
|
+
testMeadow.doReads(testMeadow.query,
|
|
657
|
+
function(pError, pQuery, pRecords)
|
|
658
|
+
{
|
|
659
|
+
// Should now be 4 (Foo Foo, Red Riding Hood, Red, Spot)
|
|
660
|
+
Expect(pRecords.length)
|
|
661
|
+
.to.equal(4);
|
|
662
|
+
var tmpIDs = pRecords.map(function(r) { return r.IDAnimal; });
|
|
663
|
+
Expect(tmpIDs).to.not.include(5);
|
|
664
|
+
fDone();
|
|
665
|
+
}
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
test
|
|
671
|
+
(
|
|
672
|
+
'Verify soft-deleted record still exists with disableDeleteTracking',
|
|
673
|
+
function(fDone)
|
|
674
|
+
{
|
|
675
|
+
var testMeadow = newMeadow();
|
|
676
|
+
|
|
677
|
+
var tmpQuery = testMeadow.query
|
|
678
|
+
.setDisableDeleteTracking(true)
|
|
679
|
+
.addFilter('IDAnimal', 5);
|
|
680
|
+
|
|
681
|
+
testMeadow.doRead(tmpQuery,
|
|
682
|
+
function(pError, pQuery, pRecord)
|
|
683
|
+
{
|
|
684
|
+
Expect(pRecord.IDAnimal)
|
|
685
|
+
.to.equal(5);
|
|
686
|
+
Expect(pRecord.Deleted)
|
|
687
|
+
.to.equal(1);
|
|
688
|
+
// Note: Meadow's Delete behavior does not propagate IDUser
|
|
689
|
+
// from meadow.setIDUser() (unlike Create and Update behaviors),
|
|
690
|
+
// so DeletingIDUser will be the default (0).
|
|
691
|
+
Expect(pRecord.DeletingIDUser)
|
|
692
|
+
.to.equal(0);
|
|
693
|
+
fDone();
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
test
|
|
700
|
+
(
|
|
701
|
+
'Count excludes deleted records',
|
|
702
|
+
function(fDone)
|
|
703
|
+
{
|
|
704
|
+
var testMeadow = newMeadow();
|
|
705
|
+
|
|
706
|
+
testMeadow.doCount(testMeadow.query,
|
|
707
|
+
function(pError, pQuery, pCount)
|
|
708
|
+
{
|
|
709
|
+
Expect(pCount)
|
|
710
|
+
.to.equal(4);
|
|
711
|
+
fDone();
|
|
712
|
+
}
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
suite
|
|
720
|
+
(
|
|
721
|
+
'Undelete Operations',
|
|
722
|
+
function()
|
|
723
|
+
{
|
|
724
|
+
test
|
|
725
|
+
(
|
|
726
|
+
'Undelete a soft-deleted record',
|
|
727
|
+
function(fDone)
|
|
728
|
+
{
|
|
729
|
+
var testMeadow = newMeadow().setIDUser(777);
|
|
730
|
+
|
|
731
|
+
var tmpQuery = testMeadow.query
|
|
732
|
+
.addFilter('IDAnimal', 5);
|
|
733
|
+
|
|
734
|
+
testMeadow.doUndelete(tmpQuery,
|
|
735
|
+
function(pError, pQuery, pCount)
|
|
736
|
+
{
|
|
737
|
+
Expect(pCount)
|
|
738
|
+
.to.equal(1);
|
|
739
|
+
fDone();
|
|
740
|
+
}
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
test
|
|
746
|
+
(
|
|
747
|
+
'Verify undeleted record is now visible',
|
|
748
|
+
function(fDone)
|
|
749
|
+
{
|
|
750
|
+
var testMeadow = newMeadow();
|
|
751
|
+
|
|
752
|
+
var tmpQuery = testMeadow.query
|
|
753
|
+
.addFilter('IDAnimal', 5);
|
|
754
|
+
|
|
755
|
+
testMeadow.doRead(tmpQuery,
|
|
756
|
+
function(pError, pQuery, pRecord)
|
|
757
|
+
{
|
|
758
|
+
Expect(pRecord.IDAnimal)
|
|
759
|
+
.to.equal(5);
|
|
760
|
+
Expect(pRecord.Deleted)
|
|
761
|
+
.to.equal(0);
|
|
762
|
+
Expect(pRecord.Name)
|
|
763
|
+
.to.equal('Gertrude');
|
|
764
|
+
fDone();
|
|
765
|
+
}
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
|
|
770
|
+
test
|
|
771
|
+
(
|
|
772
|
+
'Count now includes undeleted record',
|
|
773
|
+
function(fDone)
|
|
774
|
+
{
|
|
775
|
+
var testMeadow = newMeadow();
|
|
776
|
+
|
|
777
|
+
testMeadow.doCount(testMeadow.query,
|
|
778
|
+
function(pError, pQuery, pCount)
|
|
779
|
+
{
|
|
780
|
+
Expect(pCount)
|
|
781
|
+
.to.equal(5);
|
|
782
|
+
fDone();
|
|
783
|
+
}
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
suite
|
|
791
|
+
(
|
|
792
|
+
'Multiple Scopes',
|
|
793
|
+
function()
|
|
794
|
+
{
|
|
795
|
+
test
|
|
796
|
+
(
|
|
797
|
+
'Create records in a different scope and verify isolation',
|
|
798
|
+
function(fDone)
|
|
799
|
+
{
|
|
800
|
+
var testMeadow = require('../source/Meadow.js')
|
|
801
|
+
.new(libFable, 'Vehicle')
|
|
802
|
+
.setProvider('RocksDB')
|
|
803
|
+
.setSchema([
|
|
804
|
+
{ Column: "IDVehicle", Type:"AutoIdentity" },
|
|
805
|
+
{ Column: "GUIDVehicle", Type:"AutoGUID" },
|
|
806
|
+
{ Column: "CreateDate", Type:"CreateDate" },
|
|
807
|
+
{ Column: "CreatingIDUser", Type:"CreateIDUser" },
|
|
808
|
+
{ Column: "UpdateDate", Type:"UpdateDate" },
|
|
809
|
+
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" },
|
|
810
|
+
{ Column: "Deleted", Type:"Deleted" },
|
|
811
|
+
{ Column: "DeletingIDUser", Type:"DeleteIDUser" },
|
|
812
|
+
{ Column: "DeleteDate", Type:"DeleteDate" }
|
|
813
|
+
])
|
|
814
|
+
.setDefaultIdentifier('IDVehicle')
|
|
815
|
+
.setDefault({
|
|
816
|
+
IDVehicle: null,
|
|
817
|
+
GUIDVehicle: '',
|
|
818
|
+
CreateDate: false,
|
|
819
|
+
CreatingIDUser: 0,
|
|
820
|
+
UpdateDate: false,
|
|
821
|
+
UpdatingIDUser: 0,
|
|
822
|
+
Deleted: 0,
|
|
823
|
+
DeleteDate: false,
|
|
824
|
+
DeletingIDUser: 0,
|
|
825
|
+
Make: 'Unknown',
|
|
826
|
+
Model: 'Unknown'
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
var tmpQuery = testMeadow.query.clone()
|
|
830
|
+
.addRecord({Make:'Toyota', Model:'Camry'});
|
|
831
|
+
|
|
832
|
+
testMeadow.doCreate(tmpQuery,
|
|
833
|
+
function(pError, pQuery, pQueryRead, pRecord)
|
|
834
|
+
{
|
|
835
|
+
Expect(pRecord.Make).to.equal('Toyota');
|
|
836
|
+
Expect(pRecord.IDVehicle).to.equal(1);
|
|
837
|
+
|
|
838
|
+
// Now verify FableTest scope still has its 5 records
|
|
839
|
+
var animalMeadow = newMeadow();
|
|
840
|
+
animalMeadow.doCount(animalMeadow.query,
|
|
841
|
+
function(pError2, pQuery2, pCount)
|
|
842
|
+
{
|
|
843
|
+
Expect(pCount).to.equal(5);
|
|
844
|
+
fDone();
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
suite
|
|
855
|
+
(
|
|
856
|
+
'Error Handling',
|
|
857
|
+
function()
|
|
858
|
+
{
|
|
859
|
+
test
|
|
860
|
+
(
|
|
861
|
+
'Read with no connection should return error',
|
|
862
|
+
function(fDone)
|
|
863
|
+
{
|
|
864
|
+
var tmpFable2 = new (require('fable'))({
|
|
865
|
+
LogStreams: [{ level: 'fatal', streamtype: 'process.stdout' }]
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
var testMeadow = require('../source/Meadow.js')
|
|
869
|
+
.new(tmpFable2, 'FableTest')
|
|
870
|
+
.setProvider('RocksDB')
|
|
871
|
+
.setSchema(_AnimalSchema)
|
|
872
|
+
.setDefaultIdentifier('IDAnimal')
|
|
873
|
+
.setDefault(_AnimalDefault);
|
|
874
|
+
|
|
875
|
+
testMeadow.doReads(testMeadow.query,
|
|
876
|
+
function(pError, pQuery, pRecords)
|
|
877
|
+
{
|
|
878
|
+
Expect(pError).to.exist;
|
|
879
|
+
fDone();
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
);
|