retold-data-service 2.0.9 → 2.0.10

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Fable
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 _Settings = require(`./model/fable-configuration.json`);
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
- setup ( () => {} );
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
- 'Change some settings later...',
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
- _Fable = new libFable(_Settings);
35
- _Fable.serviceManager.addServiceType('RetoldDataService', require('../source/Retold-Data-Service.js'));
36
- let tmpRetoldDataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
330
+ _RetoldDataService.initializeService(
331
+ (pError) =>
37
332
  {
38
- FullMeadowSchemaPath: `${process.cwd()}/test/model/`,
39
- DALMeadowSchemaPath: `${process.cwd()}/test/model/meadow/`,
40
-
41
- AutoInitializeDataService: true,
42
- AutoStartOrator: true
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
- tmpRetoldDataService.initializeService(
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
- // The data service created an orator
49
- _Fable.Orator.stopService(fDone);
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
+ );