1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const jdb = require('../');
|
9 | const DataSource = jdb.DataSource;
|
10 | const should = require('./init.js');
|
11 |
|
12 | describe('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 |
|
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 |
|
303 | describe('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 |
|
369 | describe('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 |
|
455 | describe('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 |
|
515 | describe('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 |
|
572 | describe('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 |
|
630 | describe('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 |
|
647 | describe('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 | });
|