UNPKG

18.4 kBJavaScriptView Raw
1// Copyright IBM Corp. 2015,2019. All Rights Reserved.
2// Node module: loopback-datasource-juggler
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6'use strict';
7
8const jdb = require('../');
9const DataSource = jdb.DataSource;
10const should = require('./init.js');
11
12describe('Memory connector with mocked discovery', function() {
13 let ds;
14
15 before(function() {
16 ds = new DataSource({connector: 'memory'});
17
18 const models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
19 {type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP'},
20 {type: 'table', name: 'LOCATION', owner: 'STRONGLOOP'}];
21
22 ds.discoverModelDefinitions = function(options, cb) {
23 process.nextTick(function() {
24 cb(null, models);
25 });
26 };
27
28 const modelProperties = [{
29 owner: 'STRONGLOOP',
30 tableName: 'INVENTORY',
31 columnName: 'PRODUCT_ID',
32 dataType: 'varchar',
33 dataLength: 20,
34 dataPrecision: null,
35 dataScale: null,
36 nullable: 0,
37 },
38 {
39 owner: 'STRONGLOOP',
40 tableName: 'INVENTORY',
41 columnName: 'LOCATION_ID',
42 dataType: 'varchar',
43 dataLength: 20,
44 dataPrecision: null,
45 dataScale: null,
46 nullable: 0,
47 },
48 {
49 owner: 'STRONGLOOP',
50 tableName: 'INVENTORY',
51 columnName: 'AVAILABLE',
52 dataType: 'int',
53 dataLength: null,
54 dataPrecision: 10,
55 dataScale: 0,
56 nullable: 1,
57 },
58 {
59 owner: 'STRONGLOOP',
60 tableName: 'INVENTORY',
61 columnName: 'TOTAL',
62 dataType: 'int',
63 dataLength: null,
64 dataPrecision: 10,
65 dataScale: 0,
66 nullable: 1,
67 }];
68
69 ds.discoverModelProperties = function(modelName, options, cb) {
70 process.nextTick(function() {
71 cb(null, modelProperties);
72 });
73 };
74 });
75
76 it('should convert table names to pascal cases and column names to camel case', function(done) {
77 ds.discoverSchemas('INVENTORY', {}, function(err, schemas) {
78 if (err) return done(err);
79 schemas.should.have.property('STRONGLOOP.INVENTORY');
80 const s = schemas['STRONGLOOP.INVENTORY'];
81 s.name.should.be.eql('Inventory');
82 Object.keys(s.properties).should.be.eql(
83 ['productId', 'locationId', 'available', 'total'],
84 );
85 done();
86 });
87 });
88
89 it('should keep the column names the same as database', function(done) {
90 ds.discoverSchemas('INVENTORY', {disableCamelCase: true}, function(err, schemas) {
91 if (err) return done(err);
92 schemas.should.have.property('STRONGLOOP.INVENTORY');
93 const s = schemas['STRONGLOOP.INVENTORY'];
94 s.name.should.be.eql('Inventory');
95 Object.keys(s.properties).should.be.eql(
96 ['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL'],
97 );
98 done();
99 });
100 });
101
102 it('should convert table/column names with custom mapper', function(done) {
103 ds.discoverSchemas('INVENTORY', {
104 nameMapper: function(type, name) {
105 // Convert all names to lower case
106 return name.toLowerCase();
107 },
108 }, function(err, schemas) {
109 if (err) return done(err);
110 schemas.should.have.property('STRONGLOOP.INVENTORY');
111 const s = schemas['STRONGLOOP.INVENTORY'];
112 s.name.should.be.eql('inventory');
113 Object.keys(s.properties).should.be.eql(
114 ['product_id', 'location_id', 'available', 'total'],
115 );
116 done();
117 });
118 });
119
120 it('should not convert table/column names with null custom mapper',
121 function(done) {
122 ds.discoverSchemas('INVENTORY', {nameMapper: null}, function(err, schemas) {
123 if (err) return done(err);
124 schemas.should.have.property('STRONGLOOP.INVENTORY');
125 const s = schemas['STRONGLOOP.INVENTORY'];
126 s.name.should.be.eql('INVENTORY');
127 Object.keys(s.properties).should.be.eql(
128 ['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL'],
129 );
130 done();
131 });
132 });
133
134 it('should honor connector\'s discoverSchemas implementation',
135 function(done) {
136 const models = {
137 inventory: {
138 product: {type: 'string'},
139 location: {type: 'string'},
140 },
141 };
142 ds.connector.discoverSchemas = function(modelName, options, cb) {
143 process.nextTick(function() {
144 cb(null, models);
145 });
146 };
147 ds.discoverSchemas('INVENTORY', {nameMapper: null}, function(err, schemas) {
148 if (err) return done(err);
149 schemas.should.be.eql(models);
150 done();
151 });
152 });
153
154 it('should callback function, passed as options parameter',
155 function(done) {
156 const models = {
157 inventory: {
158 product: {type: 'string'},
159 location: {type: 'string'},
160 },
161 };
162 ds.connector.discoverSchemas = function(modelName, options, cb) {
163 process.nextTick(function() {
164 cb(null, models);
165 });
166 };
167
168 const options = function(err, schemas) {
169 if (err) return done(err);
170 schemas.should.be.eql(models);
171 done();
172 };
173
174 ds.discoverSchemas('INVENTORY', options);
175 });
176
177 it('should discover schemas using `discoverSchemas` - promise variant',
178 function(done) {
179 ds.connector.discoverSchemas = null;
180 ds.discoverSchemas('INVENTORY', {})
181 .then(function(schemas) {
182 schemas.should.have.property('STRONGLOOP.INVENTORY');
183
184 const s = schemas['STRONGLOOP.INVENTORY'];
185 s.name.should.be.eql('Inventory');
186
187 Object.keys(s.properties).should.be.eql(
188 ['productId', 'locationId', 'available', 'total'],
189 );
190 done();
191 })
192 .catch(function(err) {
193 done(err);
194 });
195 });
196
197 describe('discoverSchema', function() {
198 let models;
199 let schema;
200 before(function() {
201 schema = {
202 name: 'Inventory',
203 options: {
204 idInjection: false,
205 memory: {schema: 'STRONGLOOP', table: 'INVENTORY'},
206 },
207 properties: {
208 available: {
209 length: null,
210 memory: {
211 columnName: 'AVAILABLE',
212 dataLength: null,
213 dataPrecision: 10,
214 dataScale: 0,
215 dataType: 'int',
216 nullable: 1,
217 },
218 precision: 10,
219 required: false,
220 scale: 0,
221 type: undefined,
222 },
223 locationId: {
224 length: 20,
225 memory: {
226 columnName: 'LOCATION_ID',
227 dataLength: 20,
228 dataPrecision: null,
229 dataScale: null,
230 dataType: 'varchar',
231 nullable: 0,
232 },
233 precision: null,
234 required: true,
235 scale: null,
236 type: undefined,
237 },
238 productId: {
239 length: 20,
240 memory: {
241 columnName: 'PRODUCT_ID',
242 dataLength: 20,
243 dataPrecision: null,
244 dataScale: null,
245 dataType: 'varchar',
246 nullable: 0,
247 },
248 precision: null,
249 required: true,
250 scale: null,
251 type: undefined,
252 },
253 total: {
254 length: null,
255 memory: {
256 columnName: 'TOTAL',
257 dataLength: null,
258 dataPrecision: 10,
259 dataScale: 0,
260 dataType: 'int',
261 nullable: 1,
262 },
263 precision: 10,
264 required: false,
265 scale: 0,
266 type: undefined,
267 },
268 },
269 };
270 });
271
272 it('should discover schema using `discoverSchema`', function(done) {
273 ds.discoverSchema('INVENTORY', {}, function(err, schemas) {
274 if (err) return done(err);
275 schemas.should.be.eql(schema);
276 done();
277 });
278 });
279
280 it('should callback function, passed as options parameter', function(done) {
281 const options = function(err, schemas) {
282 if (err) return done(err);
283 schemas.should.be.eql(schema);
284 done();
285 };
286
287 ds.discoverSchema('INVENTORY', options);
288 });
289
290 it('should discover schema using `discoverSchema` - promise variant', function(done) {
291 ds.discoverSchema('INVENTORY', {})
292 .then(function(schemas) {
293 schemas.should.be.eql(schema);
294 done();
295 })
296 .catch(function(err) {
297 done(err);
298 });
299 });
300 });
301});
302
303describe('discoverModelDefinitions', function() {
304 let ds;
305 before(function() {
306 ds = new DataSource({connector: 'memory'});
307
308 const models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
309 {type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP'},
310 {type: 'table', name: 'LOCATION', owner: 'STRONGLOOP'}];
311
312 ds.connector.discoverModelDefinitions = function(options, cb) {
313 process.nextTick(function() {
314 cb(null, models);
315 });
316 };
317 });
318
319 it('should discover model using `discoverModelDefinitions`', function(done) {
320 ds.discoverModelDefinitions({}, function(err, schemas) {
321 if (err) return done(err);
322
323 const tableNames = schemas.map(function(s) {
324 return s.name;
325 });
326
327 tableNames.should.be.eql(
328 ['CUSTOMER', 'INVENTORY', 'LOCATION'],
329 );
330 done();
331 });
332 });
333
334 it('should callback function, passed as options parameter', function(done) {
335 const options = function(err, schemas) {
336 if (err) return done(err);
337
338 const tableNames = schemas.map(function(s) {
339 return s.name;
340 });
341
342 tableNames.should.be.eql(
343 ['CUSTOMER', 'INVENTORY', 'LOCATION'],
344 );
345 done();
346 };
347
348 ds.discoverModelDefinitions(options);
349 });
350
351 it('should discover model using `discoverModelDefinitions` - promise variant', function(done) {
352 ds.discoverModelDefinitions({})
353 .then(function(schemas) {
354 const tableNames = schemas.map(function(s) {
355 return s.name;
356 });
357
358 tableNames.should.be.eql(
359 ['CUSTOMER', 'INVENTORY', 'LOCATION'],
360 );
361 done();
362 })
363 .catch(function(err) {
364 done(err);
365 });
366 });
367});
368
369describe('discoverModelProperties', function() {
370 let ds;
371 let modelProperties;
372 before(function() {
373 ds = new DataSource({connector: 'memory'});
374
375 modelProperties = [{
376 owner: 'STRONGLOOP',
377 tableName: 'INVENTORY',
378 columnName: 'PRODUCT_ID',
379 dataType: 'varchar',
380 dataLength: 20,
381 dataPrecision: null,
382 dataScale: null,
383 nullable: 0,
384 },
385 {
386 owner: 'STRONGLOOP',
387 tableName: 'INVENTORY',
388 columnName: 'LOCATION_ID',
389 dataType: 'varchar',
390 dataLength: 20,
391 dataPrecision: null,
392 dataScale: null,
393 nullable: 0,
394 },
395 {
396 owner: 'STRONGLOOP',
397 tableName: 'INVENTORY',
398 columnName: 'AVAILABLE',
399 dataType: 'int',
400 dataLength: null,
401 dataPrecision: 10,
402 dataScale: 0,
403 nullable: 1,
404 },
405 {
406 owner: 'STRONGLOOP',
407 tableName: 'INVENTORY',
408 columnName: 'TOTAL',
409 dataType: 'int',
410 dataLength: null,
411 dataPrecision: 10,
412 dataScale: 0,
413 nullable: 1,
414 }];
415
416 ds.connector.discoverModelProperties = function(modelName, options, cb) {
417 process.nextTick(function() {
418 cb(null, modelProperties);
419 });
420 };
421 });
422
423 it('should callback function, passed as options parameter', function(done) {
424 const options = function(err, schemas) {
425 if (err) return done(err);
426
427 schemas.should.be.eql(modelProperties);
428 done();
429 };
430
431 ds.discoverModelProperties('INVENTORY', options);
432 });
433
434 it('should discover model metadata using `discoverModelProperties`', function(done) {
435 ds.discoverModelProperties('INVENTORY', {}, function(err, schemas) {
436 if (err) return done(err);
437
438 schemas.should.be.eql(modelProperties);
439 done();
440 });
441 });
442
443 it('should discover model metadata using `discoverModelProperties` - promise variant', function(done) {
444 ds.discoverModelProperties('INVENTORY', {})
445 .then(function(schemas) {
446 schemas.should.be.eql(modelProperties);
447 done();
448 })
449 .catch(function(err) {
450 done(err);
451 });
452 });
453});
454
455describe('discoverPrimaryKeys', function() {
456 let ds;
457 let modelProperties, primaryKeys;
458 before(function() {
459 ds = new DataSource({connector: 'memory'});
460
461 primaryKeys = [
462 {
463 owner: 'STRONGLOOP',
464 tableName: 'INVENTORY',
465 columnName: 'PRODUCT_ID',
466 keySeq: 1,
467 pkName: 'ID_PK',
468 },
469 {
470 owner: 'STRONGLOOP',
471 tableName: 'INVENTORY',
472 columnName: 'LOCATION_ID',
473 keySeq: 2,
474 pkName: 'ID_PK',
475 }];
476
477 ds.connector.discoverPrimaryKeys = function(modelName, options, cb) {
478 process.nextTick(function() {
479 cb(null, primaryKeys);
480 });
481 };
482 });
483
484 it('should discover primary key definitions using `discoverPrimaryKeys`', function(done) {
485 ds.discoverPrimaryKeys('INVENTORY', {}, function(err, modelPrimaryKeys) {
486 if (err) return done(err);
487
488 modelPrimaryKeys.should.be.eql(primaryKeys);
489 done();
490 });
491 });
492
493 it('should callback function, passed as options parameter', function(done) {
494 const options = function(err, modelPrimaryKeys) {
495 if (err) return done(err);
496
497 modelPrimaryKeys.should.be.eql(primaryKeys);
498 done();
499 };
500 ds.discoverPrimaryKeys('INVENTORY', options);
501 });
502
503 it('should discover primary key definitions using `discoverPrimaryKeys` - promise variant', function(done) {
504 ds.discoverPrimaryKeys('INVENTORY', {})
505 .then(function(modelPrimaryKeys) {
506 modelPrimaryKeys.should.be.eql(primaryKeys);
507 done();
508 })
509 .catch(function(err) {
510 done(err);
511 });
512 });
513});
514
515describe('discoverForeignKeys', function() {
516 let ds;
517 let modelProperties, foreignKeys;
518 before(function() {
519 ds = new DataSource({connector: 'memory'});
520
521 foreignKeys = [{
522 fkOwner: 'STRONGLOOP',
523 fkName: 'PRODUCT_FK',
524 fkTableName: 'INVENTORY',
525 fkColumnName: 'PRODUCT_ID',
526 keySeq: 1,
527 pkOwner: 'STRONGLOOP',
528 pkName: 'PRODUCT_PK',
529 pkTableName: 'PRODUCT',
530 pkColumnName: 'ID',
531 }];
532
533 ds.connector.discoverForeignKeys = function(modelName, options, cb) {
534 process.nextTick(function() {
535 cb(null, foreignKeys);
536 });
537 };
538 });
539
540 it('should discover foreign key definitions using `discoverForeignKeys`', function(done) {
541 ds.discoverForeignKeys('INVENTORY', {}, function(err, modelForeignKeys) {
542 if (err) return done(err);
543
544 modelForeignKeys.should.be.eql(foreignKeys);
545 done();
546 });
547 });
548
549 it('should callback function, passed as options parameter', function(done) {
550 const options = function(err, modelForeignKeys) {
551 if (err) return done(err);
552
553 modelForeignKeys.should.be.eql(foreignKeys);
554 done();
555 };
556
557 ds.discoverForeignKeys('INVENTORY', options);
558 });
559
560 it('should discover foreign key definitions using `discoverForeignKeys` - promise variant', function(done) {
561 ds.discoverForeignKeys('INVENTORY', {})
562 .then(function(modelForeignKeys) {
563 modelForeignKeys.should.be.eql(foreignKeys);
564 done();
565 })
566 .catch(function(err) {
567 done(err);
568 });
569 });
570});
571
572describe('discoverExportedForeignKeys', function() {
573 let ds;
574 let modelProperties, exportedForeignKeys;
575 before(function() {
576 ds = new DataSource({connector: 'memory'});
577
578 exportedForeignKeys = [{
579 fkName: 'PRODUCT_FK',
580 fkOwner: 'STRONGLOOP',
581 fkTableName: 'INVENTORY',
582 fkColumnName: 'PRODUCT_ID',
583 keySeq: 1,
584 pkName: 'PRODUCT_PK',
585 pkOwner: 'STRONGLOOP',
586 pkTableName: 'PRODUCT',
587 pkColumnName: 'ID',
588 }];
589
590 ds.connector.discoverExportedForeignKeys = function(modelName, options, cb) {
591 process.nextTick(function() {
592 cb(null, exportedForeignKeys);
593 });
594 };
595 });
596
597 it('should discover foreign key definitions using `discoverExportedForeignKeys`', function(done) {
598 ds.discoverExportedForeignKeys('INVENTORY', {}, function(err, modelForeignKeys) {
599 if (err) return done(err);
600
601 modelForeignKeys.should.be.eql(exportedForeignKeys);
602 done();
603 });
604 });
605
606 it('should callback function, passed as options parameter', function(done) {
607 const options = function(err, modelForeignKeys) {
608 if (err) return done(err);
609
610 modelForeignKeys.should.be.eql(exportedForeignKeys);
611 done();
612 };
613
614 ds.discoverExportedForeignKeys('INVENTORY', options);
615 });
616
617 it('should discover foreign key definitions using `discoverExportedForeignKeys` - promise variant',
618 function(done) {
619 ds.discoverExportedForeignKeys('INVENTORY', {})
620 .then(function(modelForeignKeys) {
621 modelForeignKeys.should.be.eql(exportedForeignKeys);
622 done();
623 })
624 .catch(function(err) {
625 done(err);
626 });
627 });
628});
629
630describe('Mock connector', function() {
631 const mockConnectors = require('./mock-connectors');
632 describe('customFieldSettings', function() {
633 const ds = new DataSource(mockConnectors.customFieldSettings);
634
635 it('should be present in discoverSchemas', function(done) {
636 ds.discoverSchemas('person', function(err, schemas) {
637 should.not.exist(err);
638 schemas.should.be.an.Object;
639 schemas['public.person'].properties.name
640 .custom.storage.should.equal('quantum');
641 done();
642 });
643 });
644 });
645});
646
647describe('Default memory connector', function() {
648 const nonExistantError = 'Table \'NONEXISTENT\' does not exist.';
649 let ds;
650
651 before(function() {
652 ds = new DataSource({connector: 'memory'});
653 });
654
655 it('discoverSchema should return an error when table does not exist', function(done) {
656 ds.discoverSchema('NONEXISTENT', {}, function(err, schemas) {
657 should.exist(err);
658 err.message.should.eql(nonExistantError);
659 done();
660 });
661 });
662
663 it('discoverSchemas should return an error when table does not exist', function(done) {
664 ds.discoverSchemas('NONEXISTENT', {}, function(err, schemas) {
665 should.exist(err);
666 err.message.should.eql(nonExistantError);
667 done();
668 });
669 });
670});