retold-data-service 2.0.9 → 2.0.11
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/CONTRIBUTING.md +50 -0
- package/README.md +200 -56
- package/docs/.nojekyll +0 -0
- package/docs/README.md +76 -0
- package/docs/_sidebar.md +13 -0
- package/docs/architecture.md +94 -0
- package/docs/behavior-injection.md +111 -0
- package/docs/configuration.md +93 -0
- package/docs/cover.md +11 -0
- package/docs/dal-access.md +87 -0
- package/docs/endpoints.md +121 -0
- package/docs/index.html +39 -0
- package/docs/initialization.md +77 -0
- package/docs/lifecycle-hooks.md +81 -0
- package/docs/schema-definition.md +128 -0
- package/docs/storage-providers.md +109 -0
- package/package.json +10 -6
- package/source/Retold-Data-Service.js +4 -4
- package/test/RetoldDataService_tests.js +1315 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unit tests for
|
|
2
|
+
* Unit tests for Retold Data Service
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
5
|
*
|
|
@@ -8,18 +8,223 @@
|
|
|
8
8
|
|
|
9
9
|
var Chai = require("chai");
|
|
10
10
|
var Expect = Chai.expect;
|
|
11
|
-
var Assert = Chai.assert;
|
|
12
11
|
|
|
13
12
|
const libFable = require('fable');
|
|
13
|
+
const libSuperTest = require('supertest');
|
|
14
|
+
const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
const _APIServerPort = 9329;
|
|
17
|
+
const _BaseURL = `http://localhost:${_APIServerPort}/`;
|
|
18
|
+
|
|
19
|
+
let _Fable;
|
|
20
|
+
let _RetoldDataService;
|
|
21
|
+
let _SuperTest;
|
|
16
22
|
|
|
17
23
|
suite
|
|
18
24
|
(
|
|
19
25
|
'Retold Data Service',
|
|
20
26
|
function()
|
|
21
27
|
{
|
|
22
|
-
|
|
28
|
+
suiteSetup
|
|
29
|
+
(
|
|
30
|
+
function(fDone)
|
|
31
|
+
{
|
|
32
|
+
this.timeout(10000);
|
|
33
|
+
|
|
34
|
+
let tmpSettings = {
|
|
35
|
+
Product: 'RetoldDataServiceTest',
|
|
36
|
+
ProductVersion: '1.0.0',
|
|
37
|
+
APIServerPort: _APIServerPort,
|
|
38
|
+
SQLite:
|
|
39
|
+
{
|
|
40
|
+
SQLiteFilePath: ':memory:'
|
|
41
|
+
},
|
|
42
|
+
LogStreams:
|
|
43
|
+
[
|
|
44
|
+
{
|
|
45
|
+
streamtype: 'console',
|
|
46
|
+
level: 'fatal'
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
_Fable = new libFable(tmpSettings);
|
|
52
|
+
|
|
53
|
+
// Register the SQLite provider
|
|
54
|
+
_Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
|
|
55
|
+
_Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
|
|
56
|
+
|
|
57
|
+
_Fable.MeadowSQLiteProvider.connectAsync(
|
|
58
|
+
(pError) =>
|
|
59
|
+
{
|
|
60
|
+
if (pError)
|
|
61
|
+
{
|
|
62
|
+
return fDone(pError);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let tmpDB = _Fable.MeadowSQLiteProvider.db;
|
|
66
|
+
|
|
67
|
+
// Create all tables for the BookStore model
|
|
68
|
+
tmpDB.exec(`
|
|
69
|
+
CREATE TABLE IF NOT EXISTS Book (
|
|
70
|
+
IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
71
|
+
GUIDBook TEXT,
|
|
72
|
+
CreateDate TEXT,
|
|
73
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
74
|
+
UpdateDate TEXT,
|
|
75
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
76
|
+
Deleted INTEGER DEFAULT 0,
|
|
77
|
+
DeleteDate TEXT,
|
|
78
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
79
|
+
Title TEXT,
|
|
80
|
+
Type TEXT,
|
|
81
|
+
Genre TEXT,
|
|
82
|
+
ISBN TEXT,
|
|
83
|
+
Language TEXT,
|
|
84
|
+
ImageURL TEXT,
|
|
85
|
+
PublicationYear INTEGER DEFAULT 0
|
|
86
|
+
);
|
|
87
|
+
CREATE TABLE IF NOT EXISTS Author (
|
|
88
|
+
IDAuthor INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
89
|
+
GUIDAuthor TEXT,
|
|
90
|
+
CreateDate TEXT,
|
|
91
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
92
|
+
UpdateDate TEXT,
|
|
93
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
94
|
+
Deleted INTEGER DEFAULT 0,
|
|
95
|
+
DeleteDate TEXT,
|
|
96
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
97
|
+
Name TEXT
|
|
98
|
+
);
|
|
99
|
+
CREATE TABLE IF NOT EXISTS BookAuthorJoin (
|
|
100
|
+
IDBookAuthorJoin INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
101
|
+
GUIDBookAuthorJoin TEXT,
|
|
102
|
+
IDBook INTEGER DEFAULT 0,
|
|
103
|
+
IDAuthor INTEGER DEFAULT 0
|
|
104
|
+
);
|
|
105
|
+
CREATE TABLE IF NOT EXISTS BookPrice (
|
|
106
|
+
IDBookPrice INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
107
|
+
GUIDBookPrice TEXT,
|
|
108
|
+
CreateDate TEXT,
|
|
109
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
110
|
+
UpdateDate TEXT,
|
|
111
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
112
|
+
Deleted INTEGER DEFAULT 0,
|
|
113
|
+
DeleteDate TEXT,
|
|
114
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
115
|
+
Price REAL DEFAULT 0,
|
|
116
|
+
StartDate TEXT,
|
|
117
|
+
EndDate TEXT,
|
|
118
|
+
Discountable INTEGER DEFAULT 0,
|
|
119
|
+
CouponCode TEXT,
|
|
120
|
+
IDBook INTEGER DEFAULT 0
|
|
121
|
+
);
|
|
122
|
+
CREATE TABLE IF NOT EXISTS Review (
|
|
123
|
+
IDReviews INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
124
|
+
GUIDReviews TEXT,
|
|
125
|
+
CreateDate TEXT,
|
|
126
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
127
|
+
UpdateDate TEXT,
|
|
128
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
129
|
+
Deleted INTEGER DEFAULT 0,
|
|
130
|
+
DeleteDate TEXT,
|
|
131
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
132
|
+
Text TEXT,
|
|
133
|
+
Rating INTEGER DEFAULT 0,
|
|
134
|
+
IDBook INTEGER DEFAULT 0
|
|
135
|
+
);
|
|
136
|
+
`);
|
|
137
|
+
|
|
138
|
+
// Seed some test data
|
|
139
|
+
let tmpInsertBook = tmpDB.prepare(
|
|
140
|
+
`INSERT INTO Book (IDBook, GUIDBook, Title, Type, Genre, ISBN, Language, PublicationYear, Deleted)
|
|
141
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)`);
|
|
142
|
+
|
|
143
|
+
tmpInsertBook.run(1, 'guid-book-001', 'Dune', 'Fiction', 'Science Fiction', '978-0441172719', 'English', 1965);
|
|
144
|
+
tmpInsertBook.run(2, 'guid-book-002', 'Neuromancer', 'Fiction', 'Science Fiction', '978-0441569595', 'English', 1984);
|
|
145
|
+
tmpInsertBook.run(3, 'guid-book-003', 'Foundation', 'Fiction', 'Science Fiction', '978-0553293357', 'English', 1951);
|
|
146
|
+
tmpInsertBook.run(4, 'guid-book-004', 'Snow Crash', 'Fiction', 'Science Fiction', '978-0553380958', 'English', 1992);
|
|
147
|
+
tmpInsertBook.run(5, 'guid-book-005', 'The Hobbit', 'Fiction', 'Fantasy', '978-0547928227', 'English', 1937);
|
|
148
|
+
|
|
149
|
+
let tmpInsertAuthor = tmpDB.prepare(
|
|
150
|
+
`INSERT INTO Author (IDAuthor, GUIDAuthor, Name, Deleted)
|
|
151
|
+
VALUES (?, ?, ?, 0)`);
|
|
152
|
+
|
|
153
|
+
tmpInsertAuthor.run(1, 'guid-author-001', 'Frank Herbert');
|
|
154
|
+
tmpInsertAuthor.run(2, 'guid-author-002', 'William Gibson');
|
|
155
|
+
tmpInsertAuthor.run(3, 'guid-author-003', 'Isaac Asimov');
|
|
156
|
+
|
|
157
|
+
let tmpInsertJoin = tmpDB.prepare(
|
|
158
|
+
`INSERT INTO BookAuthorJoin (IDBookAuthorJoin, GUIDBookAuthorJoin, IDBook, IDAuthor)
|
|
159
|
+
VALUES (?, ?, ?, ?)`);
|
|
160
|
+
|
|
161
|
+
tmpInsertJoin.run(1, 'guid-join-001', 1, 1);
|
|
162
|
+
tmpInsertJoin.run(2, 'guid-join-002', 2, 2);
|
|
163
|
+
tmpInsertJoin.run(3, 'guid-join-003', 3, 3);
|
|
164
|
+
|
|
165
|
+
let tmpInsertReview = tmpDB.prepare(
|
|
166
|
+
`INSERT INTO Review (IDReviews, GUIDReviews, Text, Rating, IDBook, Deleted)
|
|
167
|
+
VALUES (?, ?, ?, ?, ?, 0)`);
|
|
168
|
+
|
|
169
|
+
tmpInsertReview.run(1, 'guid-review-001', 'A masterpiece of science fiction', 5, 1);
|
|
170
|
+
tmpInsertReview.run(2, 'guid-review-002', 'Visionary cyberpunk', 4, 2);
|
|
171
|
+
|
|
172
|
+
// Register the RetoldDataService with SQLite config
|
|
173
|
+
_Fable.serviceManager.addServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
|
|
174
|
+
_RetoldDataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
|
|
175
|
+
{
|
|
176
|
+
FullMeadowSchemaPath: `${process.cwd()}/test/model/`,
|
|
177
|
+
FullMeadowSchemaFilename: `MeadowModel-Extended.json`,
|
|
178
|
+
DALMeadowSchemaPath: `${process.cwd()}/test/model/meadow/`,
|
|
179
|
+
|
|
180
|
+
StorageProvider: 'SQLite',
|
|
181
|
+
StorageProviderModule: 'meadow-connection-sqlite',
|
|
182
|
+
|
|
183
|
+
AutoInitializeDataService: true,
|
|
184
|
+
AutoStartOrator: true
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
_RetoldDataService.initializeService(
|
|
188
|
+
(pInitError) =>
|
|
189
|
+
{
|
|
190
|
+
if (pInitError)
|
|
191
|
+
{
|
|
192
|
+
return fDone(pInitError);
|
|
193
|
+
}
|
|
194
|
+
_SuperTest = libSuperTest(_BaseURL);
|
|
195
|
+
fDone();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
suiteTeardown
|
|
202
|
+
(
|
|
203
|
+
function(fDone)
|
|
204
|
+
{
|
|
205
|
+
this.timeout(5000);
|
|
206
|
+
// Close the database
|
|
207
|
+
if (_Fable && _Fable.MeadowSQLiteProvider && _Fable.MeadowSQLiteProvider.db)
|
|
208
|
+
{
|
|
209
|
+
try { _Fable.MeadowSQLiteProvider.db.close(); }
|
|
210
|
+
catch (pIgnore) { /* already closed */ }
|
|
211
|
+
}
|
|
212
|
+
// Close the server directly to avoid keep-alive hangs
|
|
213
|
+
if (_Fable && _Fable.OratorServiceServer && _Fable.OratorServiceServer.Active && _Fable.OratorServiceServer.server)
|
|
214
|
+
{
|
|
215
|
+
_Fable.OratorServiceServer.server.close(
|
|
216
|
+
() =>
|
|
217
|
+
{
|
|
218
|
+
_Fable.OratorServiceServer.Active = false;
|
|
219
|
+
fDone();
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else
|
|
223
|
+
{
|
|
224
|
+
fDone();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
);
|
|
23
228
|
|
|
24
229
|
suite
|
|
25
230
|
(
|
|
@@ -28,29 +233,1121 @@ suite
|
|
|
28
233
|
{
|
|
29
234
|
test
|
|
30
235
|
(
|
|
31
|
-
'
|
|
236
|
+
'RetoldDataService class should exist',
|
|
237
|
+
function()
|
|
238
|
+
{
|
|
239
|
+
let tmpRetoldDataServiceClass = require('../source/Retold-Data-Service.js');
|
|
240
|
+
Expect(tmpRetoldDataServiceClass).to.be.a('function');
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
test
|
|
244
|
+
(
|
|
245
|
+
'RetoldDataService instance should have been created',
|
|
246
|
+
function()
|
|
247
|
+
{
|
|
248
|
+
Expect(_RetoldDataService).to.be.an('object');
|
|
249
|
+
Expect(_RetoldDataService.serviceType).to.equal('RetoldDataService');
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
test
|
|
253
|
+
(
|
|
254
|
+
'RetoldDataService should be initialized',
|
|
255
|
+
function()
|
|
256
|
+
{
|
|
257
|
+
Expect(_RetoldDataService.serviceInitialized).to.equal(true);
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
test
|
|
261
|
+
(
|
|
262
|
+
'Should have loaded the full model',
|
|
263
|
+
function()
|
|
264
|
+
{
|
|
265
|
+
Expect(_RetoldDataService.fullModel).to.be.an('object');
|
|
266
|
+
Expect(_RetoldDataService.fullModel.Tables).to.be.an('object');
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
test
|
|
270
|
+
(
|
|
271
|
+
'Should have the correct entity list',
|
|
272
|
+
function()
|
|
273
|
+
{
|
|
274
|
+
Expect(_RetoldDataService.entityList).to.be.an('array');
|
|
275
|
+
Expect(_RetoldDataService.entityList).to.include('Book');
|
|
276
|
+
Expect(_RetoldDataService.entityList).to.include('Author');
|
|
277
|
+
Expect(_RetoldDataService.entityList).to.include('BookAuthorJoin');
|
|
278
|
+
Expect(_RetoldDataService.entityList).to.include('BookPrice');
|
|
279
|
+
Expect(_RetoldDataService.entityList).to.include('Review');
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
test
|
|
283
|
+
(
|
|
284
|
+
'Should have created DAL objects for each entity',
|
|
285
|
+
function()
|
|
286
|
+
{
|
|
287
|
+
Expect(_Fable.DAL).to.be.an('object');
|
|
288
|
+
Expect(_Fable.DAL.Book).to.be.an('object');
|
|
289
|
+
Expect(_Fable.DAL.Author).to.be.an('object');
|
|
290
|
+
Expect(_Fable.DAL.BookAuthorJoin).to.be.an('object');
|
|
291
|
+
Expect(_Fable.DAL.BookPrice).to.be.an('object');
|
|
292
|
+
Expect(_Fable.DAL.Review).to.be.an('object');
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
test
|
|
296
|
+
(
|
|
297
|
+
'Should have created MeadowEndpoints for each entity',
|
|
298
|
+
function()
|
|
299
|
+
{
|
|
300
|
+
Expect(_Fable.MeadowEndpoints).to.be.an('object');
|
|
301
|
+
Expect(_Fable.MeadowEndpoints.Book).to.be.an('object');
|
|
302
|
+
Expect(_Fable.MeadowEndpoints.Author).to.be.an('object');
|
|
303
|
+
Expect(_Fable.MeadowEndpoints.BookAuthorJoin).to.be.an('object');
|
|
304
|
+
Expect(_Fable.MeadowEndpoints.BookPrice).to.be.an('object');
|
|
305
|
+
Expect(_Fable.MeadowEndpoints.Review).to.be.an('object');
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
test
|
|
309
|
+
(
|
|
310
|
+
'DAL objects should be configured with SQLite provider',
|
|
311
|
+
function()
|
|
312
|
+
{
|
|
313
|
+
Expect(_Fable.DAL.Book.providerName).to.equal('SQLite');
|
|
314
|
+
Expect(_Fable.DAL.Author.providerName).to.equal('SQLite');
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
suite
|
|
321
|
+
(
|
|
322
|
+
'Service Lifecycle',
|
|
323
|
+
function()
|
|
324
|
+
{
|
|
325
|
+
test
|
|
326
|
+
(
|
|
327
|
+
'Should error when initializing an already-initialized service',
|
|
32
328
|
function(fDone)
|
|
33
329
|
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let tmpRetoldDataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
|
|
330
|
+
_RetoldDataService.initializeService(
|
|
331
|
+
(pError) =>
|
|
37
332
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
333
|
+
Expect(pError).to.be.an.instanceof(Error);
|
|
334
|
+
Expect(pError.message).to.contain('already been initialized');
|
|
335
|
+
fDone();
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
test
|
|
340
|
+
(
|
|
341
|
+
'Should have onBeforeInitialize hook',
|
|
342
|
+
function()
|
|
343
|
+
{
|
|
344
|
+
Expect(_RetoldDataService.onBeforeInitialize).to.be.a('function');
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
test
|
|
348
|
+
(
|
|
349
|
+
'Should have onInitialize hook',
|
|
350
|
+
function()
|
|
351
|
+
{
|
|
352
|
+
Expect(_RetoldDataService.onInitialize).to.be.a('function');
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
test
|
|
356
|
+
(
|
|
357
|
+
'Should have onAfterInitialize hook',
|
|
358
|
+
function()
|
|
359
|
+
{
|
|
360
|
+
Expect(_RetoldDataService.onAfterInitialize).to.be.a('function');
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
test
|
|
364
|
+
(
|
|
365
|
+
'Should have stopService method',
|
|
366
|
+
function()
|
|
367
|
+
{
|
|
368
|
+
Expect(_RetoldDataService.stopService).to.be.a('function');
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
suite
|
|
375
|
+
(
|
|
376
|
+
'Options and Configuration',
|
|
377
|
+
function()
|
|
378
|
+
{
|
|
379
|
+
test
|
|
380
|
+
(
|
|
381
|
+
'Should have default options merged',
|
|
382
|
+
function()
|
|
383
|
+
{
|
|
384
|
+
Expect(_RetoldDataService.options).to.be.an('object');
|
|
385
|
+
Expect(_RetoldDataService.options.StorageProvider).to.equal('SQLite');
|
|
386
|
+
Expect(_RetoldDataService.options.AutoStartOrator).to.equal(true);
|
|
387
|
+
Expect(_RetoldDataService.options.AutoInitializeDataService).to.equal(true);
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
test
|
|
391
|
+
(
|
|
392
|
+
'Should have correct schema paths',
|
|
393
|
+
function()
|
|
394
|
+
{
|
|
395
|
+
Expect(_RetoldDataService.options.FullMeadowSchemaPath).to.contain('test/model/');
|
|
396
|
+
Expect(_RetoldDataService.options.FullMeadowSchemaFilename).to.equal('MeadowModel-Extended.json');
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
suite
|
|
403
|
+
(
|
|
404
|
+
'Book CRUD Endpoints',
|
|
405
|
+
function()
|
|
406
|
+
{
|
|
407
|
+
test
|
|
408
|
+
(
|
|
409
|
+
'Read a single Book by ID',
|
|
410
|
+
function(fDone)
|
|
411
|
+
{
|
|
412
|
+
_SuperTest
|
|
413
|
+
.get('1.0/Book/1')
|
|
414
|
+
.expect(200)
|
|
415
|
+
.end(
|
|
416
|
+
(pError, pResponse) =>
|
|
417
|
+
{
|
|
418
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
419
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
420
|
+
Expect(tmpRecord.Title).to.equal('Dune');
|
|
421
|
+
Expect(tmpRecord.Genre).to.equal('Science Fiction');
|
|
422
|
+
Expect(tmpRecord.PublicationYear).to.equal(1965);
|
|
423
|
+
fDone();
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
test
|
|
428
|
+
(
|
|
429
|
+
'Read another Book by ID',
|
|
430
|
+
function(fDone)
|
|
431
|
+
{
|
|
432
|
+
_SuperTest
|
|
433
|
+
.get('1.0/Book/2')
|
|
434
|
+
.expect(200)
|
|
435
|
+
.end(
|
|
436
|
+
(pError, pResponse) =>
|
|
437
|
+
{
|
|
438
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
439
|
+
Expect(tmpRecord.IDBook).to.equal(2);
|
|
440
|
+
Expect(tmpRecord.Title).to.equal('Neuromancer');
|
|
441
|
+
fDone();
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
);
|
|
445
|
+
test
|
|
446
|
+
(
|
|
447
|
+
'Read multiple Books',
|
|
448
|
+
function(fDone)
|
|
449
|
+
{
|
|
450
|
+
_SuperTest
|
|
451
|
+
.get('1.0/Books')
|
|
452
|
+
.expect(200)
|
|
453
|
+
.end(
|
|
454
|
+
(pError, pResponse) =>
|
|
455
|
+
{
|
|
456
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
457
|
+
Expect(tmpRecords).to.be.an('array');
|
|
458
|
+
Expect(tmpRecords.length).to.equal(5);
|
|
459
|
+
fDone();
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
test
|
|
464
|
+
(
|
|
465
|
+
'Read Books with filter',
|
|
466
|
+
function(fDone)
|
|
467
|
+
{
|
|
468
|
+
_SuperTest
|
|
469
|
+
.get('1.0/Books/FilteredTo/FBV~Genre~EQ~Fantasy')
|
|
470
|
+
.expect(200)
|
|
471
|
+
.end(
|
|
472
|
+
(pError, pResponse) =>
|
|
473
|
+
{
|
|
474
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
475
|
+
Expect(tmpRecords).to.be.an('array');
|
|
476
|
+
Expect(tmpRecords.length).to.equal(1);
|
|
477
|
+
Expect(tmpRecords[0].Title).to.equal('The Hobbit');
|
|
478
|
+
fDone();
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
test
|
|
483
|
+
(
|
|
484
|
+
'Read Books with LIKE filter',
|
|
485
|
+
function(fDone)
|
|
486
|
+
{
|
|
487
|
+
_SuperTest
|
|
488
|
+
.get('1.0/Books/FilteredTo/FBV~Title~LK~%25Dune%25')
|
|
489
|
+
.expect(200)
|
|
490
|
+
.end(
|
|
491
|
+
(pError, pResponse) =>
|
|
492
|
+
{
|
|
493
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
494
|
+
Expect(tmpRecords).to.be.an('array');
|
|
495
|
+
Expect(tmpRecords.length).to.equal(1);
|
|
496
|
+
Expect(tmpRecords[0].Title).to.equal('Dune');
|
|
497
|
+
fDone();
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
test
|
|
502
|
+
(
|
|
503
|
+
'Read Books with pagination (cap)',
|
|
504
|
+
function(fDone)
|
|
505
|
+
{
|
|
506
|
+
_SuperTest
|
|
507
|
+
.get('1.0/Books/0/2')
|
|
508
|
+
.expect(200)
|
|
509
|
+
.end(
|
|
510
|
+
(pError, pResponse) =>
|
|
511
|
+
{
|
|
512
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
513
|
+
Expect(tmpRecords).to.be.an('array');
|
|
514
|
+
Expect(tmpRecords.length).to.equal(2);
|
|
515
|
+
fDone();
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
test
|
|
520
|
+
(
|
|
521
|
+
'Read Books with pagination (begin + cap)',
|
|
522
|
+
function(fDone)
|
|
523
|
+
{
|
|
524
|
+
_SuperTest
|
|
525
|
+
.get('1.0/Books/2/2')
|
|
526
|
+
.expect(200)
|
|
527
|
+
.end(
|
|
528
|
+
(pError, pResponse) =>
|
|
529
|
+
{
|
|
530
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
531
|
+
Expect(tmpRecords).to.be.an('array');
|
|
532
|
+
Expect(tmpRecords.length).to.equal(2);
|
|
533
|
+
fDone();
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
test
|
|
538
|
+
(
|
|
539
|
+
'Count all Books',
|
|
540
|
+
function(fDone)
|
|
541
|
+
{
|
|
542
|
+
_SuperTest
|
|
543
|
+
.get('1.0/Books/Count')
|
|
544
|
+
.expect(200)
|
|
545
|
+
.end(
|
|
546
|
+
(pError, pResponse) =>
|
|
547
|
+
{
|
|
548
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
549
|
+
Expect(tmpResult.Count).to.equal(5);
|
|
550
|
+
fDone();
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
test
|
|
555
|
+
(
|
|
556
|
+
'Count Books with filter',
|
|
557
|
+
function(fDone)
|
|
558
|
+
{
|
|
559
|
+
_SuperTest
|
|
560
|
+
.get('1.0/Books/Count/FilteredTo/FBV~Genre~EQ~Science Fiction')
|
|
561
|
+
.expect(200)
|
|
562
|
+
.end(
|
|
563
|
+
(pError, pResponse) =>
|
|
564
|
+
{
|
|
565
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
566
|
+
Expect(tmpResult.Count).to.equal(4);
|
|
567
|
+
fDone();
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
test
|
|
572
|
+
(
|
|
573
|
+
'Create a new Book',
|
|
574
|
+
function(fDone)
|
|
575
|
+
{
|
|
576
|
+
_SuperTest
|
|
577
|
+
.post('1.0/Book')
|
|
578
|
+
.send(
|
|
579
|
+
{
|
|
580
|
+
Title: 'Enders Game',
|
|
581
|
+
Type: 'Fiction',
|
|
582
|
+
Genre: 'Science Fiction',
|
|
583
|
+
ISBN: '978-0812550702',
|
|
584
|
+
Language: 'English',
|
|
585
|
+
PublicationYear: 1985
|
|
586
|
+
})
|
|
587
|
+
.expect(200)
|
|
588
|
+
.end(
|
|
589
|
+
(pError, pResponse) =>
|
|
590
|
+
{
|
|
591
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
592
|
+
Expect(tmpRecord.IDBook).to.be.greaterThan(0);
|
|
593
|
+
Expect(tmpRecord.Title).to.equal('Enders Game');
|
|
594
|
+
Expect(tmpRecord.Genre).to.equal('Science Fiction');
|
|
595
|
+
Expect(tmpRecord.GUIDBook).to.be.a('string');
|
|
596
|
+
Expect(tmpRecord.GUIDBook.length).to.be.greaterThan(5);
|
|
597
|
+
fDone();
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
);
|
|
601
|
+
test
|
|
602
|
+
(
|
|
603
|
+
'Verify Book count after create',
|
|
604
|
+
function(fDone)
|
|
605
|
+
{
|
|
606
|
+
_SuperTest
|
|
607
|
+
.get('1.0/Books/Count')
|
|
608
|
+
.expect(200)
|
|
609
|
+
.end(
|
|
610
|
+
(pError, pResponse) =>
|
|
611
|
+
{
|
|
612
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
613
|
+
Expect(tmpResult.Count).to.equal(6);
|
|
614
|
+
fDone();
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
test
|
|
619
|
+
(
|
|
620
|
+
'Update a Book',
|
|
621
|
+
function(fDone)
|
|
622
|
+
{
|
|
623
|
+
_SuperTest
|
|
624
|
+
.put('1.0/Book')
|
|
625
|
+
.send(
|
|
626
|
+
{
|
|
627
|
+
IDBook: 1,
|
|
628
|
+
Title: 'Dune (Updated Edition)',
|
|
629
|
+
PublicationYear: 1965
|
|
630
|
+
})
|
|
631
|
+
.expect(200)
|
|
632
|
+
.end(
|
|
633
|
+
(pError, pResponse) =>
|
|
634
|
+
{
|
|
635
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
636
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
637
|
+
Expect(tmpRecord.Title).to.equal('Dune (Updated Edition)');
|
|
638
|
+
fDone();
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
test
|
|
643
|
+
(
|
|
644
|
+
'Verify the update persisted',
|
|
645
|
+
function(fDone)
|
|
646
|
+
{
|
|
647
|
+
_SuperTest
|
|
648
|
+
.get('1.0/Book/1')
|
|
649
|
+
.expect(200)
|
|
650
|
+
.end(
|
|
651
|
+
(pError, pResponse) =>
|
|
652
|
+
{
|
|
653
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
654
|
+
Expect(tmpRecord.Title).to.equal('Dune (Updated Edition)');
|
|
655
|
+
fDone();
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
test
|
|
660
|
+
(
|
|
661
|
+
'Delete a Book (soft delete)',
|
|
662
|
+
function(fDone)
|
|
663
|
+
{
|
|
664
|
+
_SuperTest
|
|
665
|
+
.del('1.0/Book/5')
|
|
666
|
+
.expect(200)
|
|
667
|
+
.end(
|
|
668
|
+
(pError, pResponse) =>
|
|
669
|
+
{
|
|
670
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
671
|
+
Expect(tmpResult.Count).to.equal(1);
|
|
672
|
+
fDone();
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
test
|
|
677
|
+
(
|
|
678
|
+
'Verify deleted Book is not returned in reads',
|
|
679
|
+
function(fDone)
|
|
680
|
+
{
|
|
681
|
+
_SuperTest
|
|
682
|
+
.get('1.0/Books/Count')
|
|
683
|
+
.expect(200)
|
|
684
|
+
.end(
|
|
685
|
+
(pError, pResponse) =>
|
|
686
|
+
{
|
|
687
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
688
|
+
Expect(tmpResult.Count).to.equal(5);
|
|
689
|
+
fDone();
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
test
|
|
694
|
+
(
|
|
695
|
+
'Read a non-existent Book returns 404',
|
|
696
|
+
function(fDone)
|
|
697
|
+
{
|
|
698
|
+
_SuperTest
|
|
699
|
+
.get('1.0/Book/999')
|
|
700
|
+
.expect(404)
|
|
701
|
+
.end(
|
|
702
|
+
(pError, pResponse) =>
|
|
703
|
+
{
|
|
704
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
705
|
+
Expect(tmpResult.Error).to.equal('Record not Found');
|
|
706
|
+
fDone();
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
);
|
|
710
|
+
test
|
|
711
|
+
(
|
|
712
|
+
'Get Book schema endpoint',
|
|
713
|
+
function(fDone)
|
|
714
|
+
{
|
|
715
|
+
_SuperTest
|
|
716
|
+
.get('1.0/Book/Schema')
|
|
717
|
+
.expect(200)
|
|
718
|
+
.end(
|
|
719
|
+
(pError, pResponse) =>
|
|
720
|
+
{
|
|
721
|
+
let tmpSchema = JSON.parse(pResponse.text);
|
|
722
|
+
Expect(tmpSchema).to.be.an('object');
|
|
723
|
+
Expect(tmpSchema.title).to.equal('Book');
|
|
724
|
+
Expect(tmpSchema.properties).to.be.an('object');
|
|
725
|
+
Expect(tmpSchema.properties.Title).to.be.an('object');
|
|
726
|
+
fDone();
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
test
|
|
731
|
+
(
|
|
732
|
+
'Get a new default Book record',
|
|
733
|
+
function(fDone)
|
|
734
|
+
{
|
|
735
|
+
_SuperTest
|
|
736
|
+
.get('1.0/Book/Schema/New')
|
|
737
|
+
.expect(200)
|
|
738
|
+
.end(
|
|
739
|
+
(pError, pResponse) =>
|
|
740
|
+
{
|
|
741
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
742
|
+
Expect(tmpRecord).to.be.an('object');
|
|
743
|
+
Expect(tmpRecord.IDBook).to.equal(0);
|
|
744
|
+
Expect(tmpRecord.Title).to.equal('');
|
|
745
|
+
fDone();
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
suite
|
|
753
|
+
(
|
|
754
|
+
'Author CRUD Endpoints',
|
|
755
|
+
function()
|
|
756
|
+
{
|
|
757
|
+
test
|
|
758
|
+
(
|
|
759
|
+
'Read a single Author',
|
|
760
|
+
function(fDone)
|
|
761
|
+
{
|
|
762
|
+
_SuperTest
|
|
763
|
+
.get('1.0/Author/1')
|
|
764
|
+
.expect(200)
|
|
765
|
+
.end(
|
|
766
|
+
(pError, pResponse) =>
|
|
767
|
+
{
|
|
768
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
769
|
+
Expect(tmpRecord.IDAuthor).to.equal(1);
|
|
770
|
+
Expect(tmpRecord.Name).to.equal('Frank Herbert');
|
|
771
|
+
fDone();
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
);
|
|
775
|
+
test
|
|
776
|
+
(
|
|
777
|
+
'Read all Authors',
|
|
778
|
+
function(fDone)
|
|
779
|
+
{
|
|
780
|
+
_SuperTest
|
|
781
|
+
.get('1.0/Authors')
|
|
782
|
+
.expect(200)
|
|
783
|
+
.end(
|
|
784
|
+
(pError, pResponse) =>
|
|
785
|
+
{
|
|
786
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
787
|
+
Expect(tmpRecords).to.be.an('array');
|
|
788
|
+
Expect(tmpRecords.length).to.equal(3);
|
|
789
|
+
fDone();
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
);
|
|
793
|
+
test
|
|
794
|
+
(
|
|
795
|
+
'Create a new Author',
|
|
796
|
+
function(fDone)
|
|
797
|
+
{
|
|
798
|
+
_SuperTest
|
|
799
|
+
.post('1.0/Author')
|
|
800
|
+
.send({ Name: 'Neal Stephenson' })
|
|
801
|
+
.expect(200)
|
|
802
|
+
.end(
|
|
803
|
+
(pError, pResponse) =>
|
|
804
|
+
{
|
|
805
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
806
|
+
Expect(tmpRecord.IDAuthor).to.be.greaterThan(0);
|
|
807
|
+
Expect(tmpRecord.Name).to.equal('Neal Stephenson');
|
|
808
|
+
fDone();
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
test
|
|
813
|
+
(
|
|
814
|
+
'Update an Author',
|
|
815
|
+
function(fDone)
|
|
816
|
+
{
|
|
817
|
+
_SuperTest
|
|
818
|
+
.put('1.0/Author')
|
|
819
|
+
.send({ IDAuthor: 1, Name: 'Frank P. Herbert' })
|
|
820
|
+
.expect(200)
|
|
821
|
+
.end(
|
|
822
|
+
(pError, pResponse) =>
|
|
823
|
+
{
|
|
824
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
825
|
+
Expect(tmpRecord.IDAuthor).to.equal(1);
|
|
826
|
+
Expect(tmpRecord.Name).to.equal('Frank P. Herbert');
|
|
827
|
+
fDone();
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
);
|
|
831
|
+
test
|
|
832
|
+
(
|
|
833
|
+
'Count Authors',
|
|
834
|
+
function(fDone)
|
|
835
|
+
{
|
|
836
|
+
_SuperTest
|
|
837
|
+
.get('1.0/Authors/Count')
|
|
838
|
+
.expect(200)
|
|
839
|
+
.end(
|
|
840
|
+
(pError, pResponse) =>
|
|
841
|
+
{
|
|
842
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
843
|
+
Expect(tmpResult.Count).to.equal(4);
|
|
844
|
+
fDone();
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
test
|
|
849
|
+
(
|
|
850
|
+
'Delete an Author (soft delete)',
|
|
851
|
+
function(fDone)
|
|
852
|
+
{
|
|
853
|
+
_SuperTest
|
|
854
|
+
.del('1.0/Author/4')
|
|
855
|
+
.expect(200)
|
|
856
|
+
.end(
|
|
857
|
+
(pError, pResponse) =>
|
|
858
|
+
{
|
|
859
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
860
|
+
Expect(tmpResult.Count).to.equal(1);
|
|
861
|
+
fDone();
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
test
|
|
866
|
+
(
|
|
867
|
+
'Verify Author soft delete in count',
|
|
868
|
+
function(fDone)
|
|
869
|
+
{
|
|
870
|
+
_SuperTest
|
|
871
|
+
.get('1.0/Authors/Count')
|
|
872
|
+
.expect(200)
|
|
873
|
+
.end(
|
|
874
|
+
(pError, pResponse) =>
|
|
875
|
+
{
|
|
876
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
877
|
+
Expect(tmpResult.Count).to.equal(3);
|
|
878
|
+
fDone();
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
suite
|
|
886
|
+
(
|
|
887
|
+
'BookAuthorJoin Endpoints',
|
|
888
|
+
function()
|
|
889
|
+
{
|
|
890
|
+
test
|
|
891
|
+
(
|
|
892
|
+
'Read all BookAuthorJoins',
|
|
893
|
+
function(fDone)
|
|
894
|
+
{
|
|
895
|
+
_SuperTest
|
|
896
|
+
.get('1.0/BookAuthorJoins')
|
|
897
|
+
.expect(200)
|
|
898
|
+
.end(
|
|
899
|
+
(pError, pResponse) =>
|
|
900
|
+
{
|
|
901
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
902
|
+
Expect(tmpRecords).to.be.an('array');
|
|
903
|
+
Expect(tmpRecords.length).to.equal(3);
|
|
904
|
+
fDone();
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
);
|
|
908
|
+
test
|
|
909
|
+
(
|
|
910
|
+
'Read a single BookAuthorJoin',
|
|
911
|
+
function(fDone)
|
|
912
|
+
{
|
|
913
|
+
_SuperTest
|
|
914
|
+
.get('1.0/BookAuthorJoin/1')
|
|
915
|
+
.expect(200)
|
|
916
|
+
.end(
|
|
917
|
+
(pError, pResponse) =>
|
|
918
|
+
{
|
|
919
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
920
|
+
Expect(tmpRecord.IDBookAuthorJoin).to.equal(1);
|
|
921
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
922
|
+
Expect(tmpRecord.IDAuthor).to.equal(1);
|
|
923
|
+
fDone();
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
test
|
|
928
|
+
(
|
|
929
|
+
'Create a BookAuthorJoin',
|
|
930
|
+
function(fDone)
|
|
931
|
+
{
|
|
932
|
+
_SuperTest
|
|
933
|
+
.post('1.0/BookAuthorJoin')
|
|
934
|
+
.send({ IDBook: 4, IDAuthor: 4 })
|
|
935
|
+
.expect(200)
|
|
936
|
+
.end(
|
|
937
|
+
(pError, pResponse) =>
|
|
938
|
+
{
|
|
939
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
940
|
+
Expect(tmpRecord.IDBookAuthorJoin).to.be.greaterThan(0);
|
|
941
|
+
Expect(tmpRecord.IDBook).to.equal(4);
|
|
942
|
+
Expect(tmpRecord.IDAuthor).to.equal(4);
|
|
943
|
+
fDone();
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
);
|
|
949
|
+
|
|
950
|
+
suite
|
|
951
|
+
(
|
|
952
|
+
'Review Endpoints',
|
|
953
|
+
function()
|
|
954
|
+
{
|
|
955
|
+
test
|
|
956
|
+
(
|
|
957
|
+
'Read all Reviews',
|
|
958
|
+
function(fDone)
|
|
959
|
+
{
|
|
960
|
+
_SuperTest
|
|
961
|
+
.get('1.0/Reviews')
|
|
962
|
+
.expect(200)
|
|
963
|
+
.end(
|
|
964
|
+
(pError, pResponse) =>
|
|
965
|
+
{
|
|
966
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
967
|
+
Expect(tmpRecords).to.be.an('array');
|
|
968
|
+
Expect(tmpRecords.length).to.equal(2);
|
|
969
|
+
fDone();
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
);
|
|
973
|
+
test
|
|
974
|
+
(
|
|
975
|
+
'Read a single Review',
|
|
976
|
+
function(fDone)
|
|
977
|
+
{
|
|
978
|
+
_SuperTest
|
|
979
|
+
.get('1.0/Review/1')
|
|
980
|
+
.expect(200)
|
|
981
|
+
.end(
|
|
982
|
+
(pError, pResponse) =>
|
|
983
|
+
{
|
|
984
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
985
|
+
Expect(tmpRecord.IDReviews).to.equal(1);
|
|
986
|
+
Expect(tmpRecord.Rating).to.equal(5);
|
|
987
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
988
|
+
fDone();
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
test
|
|
993
|
+
(
|
|
994
|
+
'Create a Review',
|
|
995
|
+
function(fDone)
|
|
996
|
+
{
|
|
997
|
+
_SuperTest
|
|
998
|
+
.post('1.0/Review')
|
|
999
|
+
.send(
|
|
1000
|
+
{
|
|
1001
|
+
Text: 'Great classic sci-fi',
|
|
1002
|
+
Rating: 4,
|
|
1003
|
+
IDBook: 3
|
|
1004
|
+
})
|
|
1005
|
+
.expect(200)
|
|
1006
|
+
.end(
|
|
1007
|
+
(pError, pResponse) =>
|
|
1008
|
+
{
|
|
1009
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
1010
|
+
Expect(tmpRecord.IDReviews).to.be.greaterThan(0);
|
|
1011
|
+
Expect(tmpRecord.Text).to.equal('Great classic sci-fi');
|
|
1012
|
+
Expect(tmpRecord.Rating).to.equal(4);
|
|
1013
|
+
Expect(tmpRecord.IDBook).to.equal(3);
|
|
1014
|
+
fDone();
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
);
|
|
1018
|
+
test
|
|
1019
|
+
(
|
|
1020
|
+
'Count Reviews',
|
|
1021
|
+
function(fDone)
|
|
1022
|
+
{
|
|
1023
|
+
_SuperTest
|
|
1024
|
+
.get('1.0/Reviews/Count')
|
|
1025
|
+
.expect(200)
|
|
1026
|
+
.end(
|
|
1027
|
+
(pError, pResponse) =>
|
|
1028
|
+
{
|
|
1029
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
1030
|
+
Expect(tmpResult.Count).to.equal(3);
|
|
1031
|
+
fDone();
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
test
|
|
1036
|
+
(
|
|
1037
|
+
'Filter Reviews by IDBook',
|
|
1038
|
+
function(fDone)
|
|
1039
|
+
{
|
|
1040
|
+
_SuperTest
|
|
1041
|
+
.get('1.0/Reviews/FilteredTo/FBV~IDBook~EQ~1')
|
|
1042
|
+
.expect(200)
|
|
1043
|
+
.end(
|
|
1044
|
+
(pError, pResponse) =>
|
|
1045
|
+
{
|
|
1046
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
1047
|
+
Expect(tmpRecords).to.be.an('array');
|
|
1048
|
+
Expect(tmpRecords.length).to.equal(1);
|
|
1049
|
+
Expect(tmpRecords[0].IDBook).to.equal(1);
|
|
1050
|
+
fDone();
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
);
|
|
1056
|
+
|
|
1057
|
+
suite
|
|
1058
|
+
(
|
|
1059
|
+
'BookPrice Endpoints',
|
|
1060
|
+
function()
|
|
1061
|
+
{
|
|
1062
|
+
test
|
|
1063
|
+
(
|
|
1064
|
+
'Create a BookPrice',
|
|
1065
|
+
function(fDone)
|
|
1066
|
+
{
|
|
1067
|
+
_SuperTest
|
|
1068
|
+
.post('1.0/BookPrice')
|
|
1069
|
+
.send(
|
|
1070
|
+
{
|
|
1071
|
+
Price: 14.99,
|
|
1072
|
+
CouponCode: 'SAVE10',
|
|
1073
|
+
IDBook: 1,
|
|
1074
|
+
Discountable: 1
|
|
1075
|
+
})
|
|
1076
|
+
.expect(200)
|
|
1077
|
+
.end(
|
|
1078
|
+
(pError, pResponse) =>
|
|
1079
|
+
{
|
|
1080
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
1081
|
+
Expect(tmpRecord.IDBookPrice).to.be.greaterThan(0);
|
|
1082
|
+
Expect(tmpRecord.Price).to.equal(14.99);
|
|
1083
|
+
Expect(tmpRecord.CouponCode).to.equal('SAVE10');
|
|
1084
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
1085
|
+
fDone();
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
test
|
|
1090
|
+
(
|
|
1091
|
+
'Read BookPrices',
|
|
1092
|
+
function(fDone)
|
|
1093
|
+
{
|
|
1094
|
+
_SuperTest
|
|
1095
|
+
.get('1.0/BookPrices')
|
|
1096
|
+
.expect(200)
|
|
1097
|
+
.end(
|
|
1098
|
+
(pError, pResponse) =>
|
|
1099
|
+
{
|
|
1100
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
1101
|
+
Expect(tmpRecords).to.be.an('array');
|
|
1102
|
+
Expect(tmpRecords.length).to.equal(1);
|
|
1103
|
+
fDone();
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
test
|
|
1108
|
+
(
|
|
1109
|
+
'Update a BookPrice',
|
|
1110
|
+
function(fDone)
|
|
1111
|
+
{
|
|
1112
|
+
_SuperTest
|
|
1113
|
+
.put('1.0/BookPrice')
|
|
1114
|
+
.send(
|
|
1115
|
+
{
|
|
1116
|
+
IDBookPrice: 1,
|
|
1117
|
+
Price: 12.99,
|
|
1118
|
+
CouponCode: 'SUPERSAVE'
|
|
1119
|
+
})
|
|
1120
|
+
.expect(200)
|
|
1121
|
+
.end(
|
|
1122
|
+
(pError, pResponse) =>
|
|
1123
|
+
{
|
|
1124
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
1125
|
+
Expect(tmpRecord.IDBookPrice).to.equal(1);
|
|
1126
|
+
Expect(tmpRecord.Price).to.equal(12.99);
|
|
1127
|
+
Expect(tmpRecord.CouponCode).to.equal('SUPERSAVE');
|
|
1128
|
+
fDone();
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
suite
|
|
1136
|
+
(
|
|
1137
|
+
'DAL Direct Access',
|
|
1138
|
+
function()
|
|
1139
|
+
{
|
|
1140
|
+
test
|
|
1141
|
+
(
|
|
1142
|
+
'Should be able to query through DAL directly',
|
|
1143
|
+
function(fDone)
|
|
1144
|
+
{
|
|
1145
|
+
let tmpQuery = _Fable.DAL.Book.query
|
|
1146
|
+
.addFilter('IDBook', 1);
|
|
1147
|
+
_Fable.DAL.Book.doRead(tmpQuery,
|
|
1148
|
+
(pError, pQuery, pRecord) =>
|
|
1149
|
+
{
|
|
1150
|
+
Expect(pError).to.equal(null);
|
|
1151
|
+
Expect(pRecord.IDBook).to.equal(1);
|
|
1152
|
+
Expect(pRecord.Title).to.equal('Dune (Updated Edition)');
|
|
1153
|
+
fDone();
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
);
|
|
1157
|
+
test
|
|
1158
|
+
(
|
|
1159
|
+
'Should be able to doReads through DAL',
|
|
1160
|
+
function(fDone)
|
|
1161
|
+
{
|
|
1162
|
+
let tmpQuery = _Fable.DAL.Author.query
|
|
1163
|
+
.setCap(10);
|
|
1164
|
+
_Fable.DAL.Author.doReads(tmpQuery,
|
|
1165
|
+
(pError, pQuery, pRecords) =>
|
|
1166
|
+
{
|
|
1167
|
+
Expect(pError).to.equal(null);
|
|
1168
|
+
Expect(pRecords).to.be.an('array');
|
|
1169
|
+
Expect(pRecords.length).to.equal(3);
|
|
1170
|
+
fDone();
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
test
|
|
1175
|
+
(
|
|
1176
|
+
'Should be able to doCount through DAL',
|
|
1177
|
+
function(fDone)
|
|
1178
|
+
{
|
|
1179
|
+
let tmpQuery = _Fable.DAL.Review.query;
|
|
1180
|
+
_Fable.DAL.Review.doCount(tmpQuery,
|
|
1181
|
+
(pError, pQuery, pCount) =>
|
|
1182
|
+
{
|
|
1183
|
+
Expect(pError).to.equal(null);
|
|
1184
|
+
Expect(pCount).to.equal(3);
|
|
1185
|
+
fDone();
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
|
|
1192
|
+
suite
|
|
1193
|
+
(
|
|
1194
|
+
'Behavior Injection',
|
|
1195
|
+
function()
|
|
1196
|
+
{
|
|
1197
|
+
test
|
|
1198
|
+
(
|
|
1199
|
+
'Should support behavior injection on endpoints',
|
|
1200
|
+
function(fDone)
|
|
1201
|
+
{
|
|
1202
|
+
let tmpPreReadCalled = false;
|
|
1203
|
+
|
|
1204
|
+
_Fable.MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior(
|
|
1205
|
+
'Read-PreOperation',
|
|
1206
|
+
(pRequest, pRequestState, fRequestComplete) =>
|
|
1207
|
+
{
|
|
1208
|
+
tmpPreReadCalled = true;
|
|
1209
|
+
return fRequestComplete(false);
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
_SuperTest
|
|
1213
|
+
.get('1.0/Book/1')
|
|
1214
|
+
.expect(200)
|
|
1215
|
+
.end(
|
|
1216
|
+
(pError, pResponse) =>
|
|
1217
|
+
{
|
|
1218
|
+
Expect(tmpPreReadCalled).to.equal(true);
|
|
1219
|
+
let tmpRecord = JSON.parse(pResponse.text);
|
|
1220
|
+
Expect(tmpRecord.IDBook).to.equal(1);
|
|
1221
|
+
|
|
1222
|
+
// Clean up the behavior
|
|
1223
|
+
delete _Fable.MeadowEndpoints.Book.controller.BehaviorInjection._BehaviorFunctions['Read-PreOperation'];
|
|
1224
|
+
fDone();
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
);
|
|
1228
|
+
test
|
|
1229
|
+
(
|
|
1230
|
+
'Should support post-operation behavior injection',
|
|
1231
|
+
function(fDone)
|
|
1232
|
+
{
|
|
1233
|
+
let tmpPostReadCalled = false;
|
|
1234
|
+
|
|
1235
|
+
_Fable.MeadowEndpoints.Author.controller.BehaviorInjection.setBehavior(
|
|
1236
|
+
'Reads-PostOperation',
|
|
1237
|
+
(pRequest, pRequestState, fRequestComplete) =>
|
|
1238
|
+
{
|
|
1239
|
+
tmpPostReadCalled = true;
|
|
1240
|
+
return fRequestComplete(false);
|
|
43
1241
|
});
|
|
44
1242
|
|
|
45
|
-
|
|
46
|
-
()
|
|
1243
|
+
_SuperTest
|
|
1244
|
+
.get('1.0/Authors')
|
|
1245
|
+
.expect(200)
|
|
1246
|
+
.end(
|
|
1247
|
+
(pError, pResponse) =>
|
|
1248
|
+
{
|
|
1249
|
+
Expect(tmpPostReadCalled).to.equal(true);
|
|
1250
|
+
let tmpRecords = JSON.parse(pResponse.text);
|
|
1251
|
+
Expect(tmpRecords).to.be.an('array');
|
|
1252
|
+
|
|
1253
|
+
// Clean up the behavior
|
|
1254
|
+
delete _Fable.MeadowEndpoints.Author.controller.BehaviorInjection._BehaviorFunctions['Reads-PostOperation'];
|
|
1255
|
+
fDone();
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
);
|
|
1261
|
+
|
|
1262
|
+
suite
|
|
1263
|
+
(
|
|
1264
|
+
'Cross-Entity Operations',
|
|
1265
|
+
function()
|
|
1266
|
+
{
|
|
1267
|
+
test
|
|
1268
|
+
(
|
|
1269
|
+
'Should serve all entity endpoints simultaneously',
|
|
1270
|
+
function(fDone)
|
|
1271
|
+
{
|
|
1272
|
+
// Hit multiple endpoints to prove the service serves all entities
|
|
1273
|
+
let tmpCompleted = 0;
|
|
1274
|
+
let tmpTotal = 3;
|
|
1275
|
+
|
|
1276
|
+
let checkDone = () =>
|
|
1277
|
+
{
|
|
1278
|
+
tmpCompleted++;
|
|
1279
|
+
if (tmpCompleted >= tmpTotal)
|
|
1280
|
+
{
|
|
1281
|
+
fDone();
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
_SuperTest
|
|
1286
|
+
.get('1.0/Books/Count')
|
|
1287
|
+
.expect(200)
|
|
1288
|
+
.end(
|
|
1289
|
+
(pError, pResponse) =>
|
|
1290
|
+
{
|
|
1291
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
1292
|
+
Expect(tmpResult.Count).to.be.greaterThan(0);
|
|
1293
|
+
checkDone();
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
_SuperTest
|
|
1297
|
+
.get('1.0/Authors/Count')
|
|
1298
|
+
.expect(200)
|
|
1299
|
+
.end(
|
|
1300
|
+
(pError, pResponse) =>
|
|
1301
|
+
{
|
|
1302
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
1303
|
+
Expect(tmpResult.Count).to.be.greaterThan(0);
|
|
1304
|
+
checkDone();
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
_SuperTest
|
|
1308
|
+
.get('1.0/Reviews/Count')
|
|
1309
|
+
.expect(200)
|
|
1310
|
+
.end(
|
|
1311
|
+
(pError, pResponse) =>
|
|
1312
|
+
{
|
|
1313
|
+
let tmpResult = JSON.parse(pResponse.text);
|
|
1314
|
+
Expect(tmpResult.Count).to.be.greaterThan(0);
|
|
1315
|
+
checkDone();
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
);
|
|
1321
|
+
|
|
1322
|
+
suite
|
|
1323
|
+
(
|
|
1324
|
+
'Error Handling',
|
|
1325
|
+
function()
|
|
1326
|
+
{
|
|
1327
|
+
test
|
|
1328
|
+
(
|
|
1329
|
+
'Should error when stopping an uninitialized service',
|
|
1330
|
+
function(fDone)
|
|
1331
|
+
{
|
|
1332
|
+
let tmpFable = new libFable({LogStreams: [{streamtype: 'console', level: 'fatal'}]});
|
|
1333
|
+
tmpFable.serviceManager.addServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
|
|
1334
|
+
let tmpService = tmpFable.serviceManager.instantiateServiceProvider('RetoldDataService',
|
|
1335
|
+
{
|
|
1336
|
+
AutoStartOrator: false,
|
|
1337
|
+
FullMeadowSchemaPath: `${process.cwd()}/test/model/`,
|
|
1338
|
+
FullMeadowSchemaFilename: `MeadowModel-Extended.json`
|
|
1339
|
+
});
|
|
1340
|
+
// Don't initialize, just try to stop
|
|
1341
|
+
tmpService.stopService(
|
|
1342
|
+
(pError) =>
|
|
47
1343
|
{
|
|
48
|
-
|
|
49
|
-
|
|
1344
|
+
Expect(pError).to.be.an.instanceof(Error);
|
|
1345
|
+
Expect(pError.message).to.contain('not initialized');
|
|
1346
|
+
fDone();
|
|
50
1347
|
});
|
|
51
1348
|
}
|
|
52
1349
|
);
|
|
53
1350
|
}
|
|
54
1351
|
);
|
|
55
1352
|
}
|
|
56
|
-
);
|
|
1353
|
+
);
|