UNPKG

24.6 kBJavaScriptView Raw
1// Copyright IBM Corp. 2014,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// This test written in mocha+should.js
7'use strict';
8
9/* global getSchema:false */
10const should = require('./init.js');
11const async = require('async');
12
13let db, Category, Product, Tool, Widget, Thing, Person;
14
15// This test requires a connector that can
16// handle a custom collection or table name
17
18// TODO [fabien] add table for pgsql/mysql
19// TODO [fabien] change model definition - see #293
20
21const setupProducts = function(ids, done) {
22 async.series([
23 function(next) {
24 Tool.create({name: 'Tool Z'}, function(err, inst) {
25 ids.toolZ = inst.id;
26 next();
27 });
28 },
29 function(next) {
30 Widget.create({name: 'Widget Z'}, function(err, inst) {
31 ids.widgetZ = inst.id;
32 next();
33 });
34 },
35 function(next) {
36 Tool.create({name: 'Tool A', active: false}, function(err, inst) {
37 ids.toolA = inst.id;
38 next();
39 });
40 },
41 function(next) {
42 Widget.create({name: 'Widget A'}, function(err, inst) {
43 ids.widgetA = inst.id;
44 next();
45 });
46 },
47 function(next) {
48 Widget.create({name: 'Widget B', active: false}, function(err, inst) {
49 ids.widgetB = inst.id;
50 next();
51 });
52 },
53 ], done);
54};
55
56describe('default scope', function() {
57 before(function(done) {
58 db = getSchema();
59
60 Category = db.define('Category', {
61 name: String,
62 });
63
64 Product = db.define('Product', {
65 name: String,
66 kind: String,
67 description: String,
68 active: {type: Boolean, default: true},
69 }, {
70 scope: {order: 'name'},
71 scopes: {active: {where: {active: true}}},
72 });
73
74 Product.lookupModel = function(data) {
75 const m = this.dataSource.models[data.kind];
76 if (m.base === this) return m;
77 return this;
78 };
79
80 Tool = db.define('Tool', Product.definition.properties, {
81 base: 'Product',
82 scope: {where: {kind: 'Tool'}, order: 'name'},
83 scopes: {active: {where: {active: true}}},
84 arangodb: {collection: 'Product'},
85 mongodb: {collection: 'Product'},
86 memory: {collection: 'Product'},
87 });
88
89 Widget = db.define('Widget', Product.definition.properties, {
90 base: 'Product',
91 properties: {kind: 'Widget'},
92 scope: {where: {kind: 'Widget'}, order: 'name'},
93 scopes: {active: {where: {active: true}}},
94 arangodb: {collection: 'Product'},
95 mongodb: {collection: 'Product'},
96 memory: {collection: 'Product'},
97 });
98
99 Person = db.define('Person', {name: String}, {
100 scope: {include: 'things'},
101 forceId: false,
102 });
103
104 // inst is only valid for instance methods
105 // like save, updateAttributes
106
107 const scopeFn = function(target, inst) {
108 return {where: {kind: this.modelName}};
109 };
110
111 const propertiesFn = function(target, inst) {
112 return {kind: this.modelName};
113 };
114
115 Thing = db.define('Thing', Product.definition.properties, {
116 base: 'Product',
117 attributes: propertiesFn,
118 scope: scopeFn,
119 arangodb: {collection: 'Product'},
120 mongodb: {collection: 'Product'},
121 memory: {collection: 'Product'},
122 });
123
124 Category.hasMany(Product);
125 Category.hasMany(Tool, {scope: {order: 'name DESC'}});
126 Category.hasMany(Widget);
127 Category.hasMany(Thing);
128
129 Product.belongsTo(Category);
130 Tool.belongsTo(Category);
131 Widget.belongsTo(Category);
132 Thing.belongsTo(Category);
133
134 Person.hasMany(Thing);
135 Thing.belongsTo(Person);
136
137 db.automigrate(done);
138 });
139
140 describe('manipulation', function() {
141 const ids = {};
142
143 before(function(done) {
144 db.automigrate(done);
145 });
146
147 it('should return a scoped instance', function() {
148 const p = new Tool({name: 'Product A', kind: 'ignored'});
149 p.name.should.equal('Product A');
150 p.kind.should.equal('Tool');
151 p.setAttributes({kind: 'ignored'});
152 p.kind.should.equal('Tool');
153
154 p.setAttribute('kind', 'other'); // currently not enforced
155 p.kind.should.equal('other');
156 });
157
158 it('should create a scoped instance - tool', function(done) {
159 Tool.create({name: 'Product A', kind: 'ignored'}, function(err, p) {
160 should.not.exist(err);
161 p.name.should.equal('Product A');
162 p.kind.should.equal('Tool');
163 ids.productA = p.id;
164 done();
165 });
166 });
167
168 it('should create a scoped instance - widget', function(done) {
169 Widget.create({name: 'Product B', kind: 'ignored'}, function(err, p) {
170 should.not.exist(err);
171 p.name.should.equal('Product B');
172 p.kind.should.equal('Widget');
173 ids.productB = p.id;
174 done();
175 });
176 });
177
178 it('should update a scoped instance - updateAttributes', function(done) {
179 Tool.findById(ids.productA, function(err, p) {
180 p.updateAttributes({description: 'A thing...', kind: 'ingored'}, function(err, inst) {
181 should.not.exist(err);
182 p.name.should.equal('Product A');
183 p.kind.should.equal('Tool');
184 p.description.should.equal('A thing...');
185 done();
186 });
187 });
188 });
189
190 it('should update a scoped instance - save', function(done) {
191 Tool.findById(ids.productA, function(err, p) {
192 p.description = 'Something...';
193 p.kind = 'ignored';
194 p.save(function(err, inst) {
195 should.not.exist(err);
196 p.name.should.equal('Product A');
197 p.kind.should.equal('Tool');
198 p.description.should.equal('Something...');
199 Tool.findById(ids.productA, function(err, p) {
200 p.kind.should.equal('Tool');
201 done();
202 });
203 });
204 });
205 });
206
207 it('should update a scoped instance - updateOrCreate', function(done) {
208 const data = {id: ids.productA, description: 'Anything...', kind: 'ingored'};
209 Tool.updateOrCreate(data, function(err, p) {
210 should.not.exist(err);
211 p.name.should.equal('Product A');
212 p.kind.should.equal('Tool');
213 p.description.should.equal('Anything...');
214 done();
215 });
216 });
217 });
218
219 describe('findById', function() {
220 const ids = {};
221
222 before(function(done) {
223 db.automigrate(setupProducts.bind(null, ids, done));
224 });
225
226 it('should apply default scope', function(done) {
227 Product.findById(ids.toolA, function(err, inst) {
228 should.not.exist(err);
229 inst.name.should.equal('Tool A');
230 inst.should.be.instanceof(Tool);
231 done();
232 });
233 });
234
235 it('should apply default scope - tool', function(done) {
236 Tool.findById(ids.toolA, function(err, inst) {
237 should.not.exist(err);
238 inst.name.should.equal('Tool A');
239 done();
240 });
241 });
242
243 it('should apply default scope (no match)', function(done) {
244 Widget.findById(ids.toolA, function(err, inst) {
245 should.not.exist(err);
246 should.not.exist(inst);
247 done();
248 });
249 });
250 });
251
252 describe('find', function() {
253 const ids = {};
254
255 before(function(done) {
256 db.automigrate(setupProducts.bind(null, ids, done));
257 });
258
259 it('should apply default scope - order', function(done) {
260 Product.find(function(err, products) {
261 should.not.exist(err);
262 products.should.have.length(5);
263 products[0].name.should.equal('Tool A');
264 products[1].name.should.equal('Tool Z');
265 products[2].name.should.equal('Widget A');
266 products[3].name.should.equal('Widget B');
267 products[4].name.should.equal('Widget Z');
268
269 products[0].should.be.instanceof(Product);
270 products[0].should.be.instanceof(Tool);
271
272 products[2].should.be.instanceof(Product);
273 products[2].should.be.instanceof(Widget);
274
275 done();
276 });
277 });
278
279 it('should apply default scope - order override', function(done) {
280 Product.find({order: 'name DESC'}, function(err, products) {
281 should.not.exist(err);
282 products.should.have.length(5);
283 products[0].name.should.equal('Widget Z');
284 products[1].name.should.equal('Widget B');
285 products[2].name.should.equal('Widget A');
286 products[3].name.should.equal('Tool Z');
287 products[4].name.should.equal('Tool A');
288 done();
289 });
290 });
291
292 it('should apply default scope - tool', function(done) {
293 Tool.find(function(err, products) {
294 should.not.exist(err);
295 products.should.have.length(2);
296 products[0].name.should.equal('Tool A');
297 products[1].name.should.equal('Tool Z');
298 done();
299 });
300 });
301
302 it('should apply default scope - where (widget)', function(done) {
303 Widget.find({where: {active: true}}, function(err, products) {
304 should.not.exist(err);
305 products.should.have.length(2);
306 products[0].name.should.equal('Widget A');
307 products[1].name.should.equal('Widget Z');
308 done();
309 });
310 });
311
312 it('should apply default scope - order (widget)', function(done) {
313 Widget.find({order: 'name DESC'}, function(err, products) {
314 should.not.exist(err);
315 products.should.have.length(3);
316 products[0].name.should.equal('Widget Z');
317 products[1].name.should.equal('Widget B');
318 products[2].name.should.equal('Widget A');
319 done();
320 });
321 });
322 });
323
324 describe('exists', function() {
325 const ids = {};
326
327 before(function(done) {
328 db.automigrate(setupProducts.bind(null, ids, done));
329 });
330
331 it('should apply default scope', function(done) {
332 Product.exists(ids.widgetA, function(err, exists) {
333 should.not.exist(err);
334 exists.should.be.true;
335 done();
336 });
337 });
338
339 it('should apply default scope - tool', function(done) {
340 Tool.exists(ids.toolZ, function(err, exists) {
341 should.not.exist(err);
342 exists.should.be.true;
343 done();
344 });
345 });
346
347 it('should apply default scope - widget', function(done) {
348 Widget.exists(ids.widgetA, function(err, exists) {
349 should.not.exist(err);
350 exists.should.be.true;
351 done();
352 });
353 });
354
355 it('should apply default scope - tool (no match)', function(done) {
356 Tool.exists(ids.widgetA, function(err, exists) {
357 should.not.exist(err);
358 exists.should.be.false;
359 done();
360 });
361 });
362
363 it('should apply default scope - widget (no match)', function(done) {
364 Widget.exists(ids.toolZ, function(err, exists) {
365 should.not.exist(err);
366 exists.should.be.false;
367 done();
368 });
369 });
370 });
371
372 describe('count', function() {
373 const ids = {};
374
375 before(function(done) {
376 db.automigrate(setupProducts.bind(null, ids, done));
377 });
378
379 it('should apply default scope - order', function(done) {
380 Product.count(function(err, count) {
381 should.not.exist(err);
382 count.should.equal(5);
383 done();
384 });
385 });
386
387 it('should apply default scope - tool', function(done) {
388 Tool.count(function(err, count) {
389 should.not.exist(err);
390 count.should.equal(2);
391 done();
392 });
393 });
394
395 it('should apply default scope - widget', function(done) {
396 Widget.count(function(err, count) {
397 should.not.exist(err);
398 count.should.equal(3);
399 done();
400 });
401 });
402
403 it('should apply default scope - where', function(done) {
404 Widget.count({name: 'Widget Z'}, function(err, count) {
405 should.not.exist(err);
406 count.should.equal(1);
407 done();
408 });
409 });
410
411 it('should apply default scope - no match', function(done) {
412 Tool.count({name: 'Widget Z'}, function(err, count) {
413 should.not.exist(err);
414 count.should.equal(0);
415 done();
416 });
417 });
418 });
419
420 describe('removeById', function() {
421 const ids = {};
422
423 function isDeleted(id, done) {
424 Product.exists(id, function(err, exists) {
425 should.not.exist(err);
426 exists.should.be.false;
427 done();
428 });
429 }
430
431 before(function(done) {
432 db.automigrate(setupProducts.bind(null, ids, done));
433 });
434
435 it('should apply default scope', function(done) {
436 Product.removeById(ids.widgetZ, function(err) {
437 should.not.exist(err);
438 isDeleted(ids.widgetZ, done);
439 });
440 });
441
442 it('should apply default scope - tool', function(done) {
443 Tool.removeById(ids.toolA, function(err) {
444 should.not.exist(err);
445 isDeleted(ids.toolA, done);
446 });
447 });
448
449 it('should apply default scope - no match', function(done) {
450 Tool.removeById(ids.widgetA, function(err) {
451 should.not.exist(err);
452 Product.exists(ids.widgetA, function(err, exists) {
453 should.not.exist(err);
454 exists.should.be.true;
455 done();
456 });
457 });
458 });
459
460 it('should apply default scope - widget', function(done) {
461 Widget.removeById(ids.widgetA, function(err) {
462 should.not.exist(err);
463 isDeleted(ids.widgetA, done);
464 });
465 });
466
467 it('should apply default scope - verify', function(done) {
468 Product.find(function(err, products) {
469 should.not.exist(err);
470 products.should.have.length(2);
471 products[0].name.should.equal('Tool Z');
472 products[1].name.should.equal('Widget B');
473 done();
474 });
475 });
476 });
477
478 describe('update', function() {
479 const ids = {};
480
481 before(function(done) {
482 db.automigrate(setupProducts.bind(null, ids, done));
483 });
484
485 it('should apply default scope', function(done) {
486 Widget.update({active: false}, {active: true, kind: 'ignored'}, function(err) {
487 should.not.exist(err);
488 Widget.find({where: {active: true}}, function(err, products) {
489 should.not.exist(err);
490 products.should.have.length(3);
491 products[0].name.should.equal('Widget A');
492 products[1].name.should.equal('Widget B');
493 products[2].name.should.equal('Widget Z');
494 done();
495 });
496 });
497 });
498
499 it('should apply default scope - no match', function(done) {
500 Tool.update({name: 'Widget A'}, {name: 'Ignored'}, function(err) {
501 should.not.exist(err);
502 Product.findById(ids.widgetA, function(err, product) {
503 should.not.exist(err);
504 product.name.should.equal('Widget A');
505 done();
506 });
507 });
508 });
509
510 it('should have updated within scope', function(done) {
511 Product.find({where: {active: true}}, function(err, products) {
512 should.not.exist(err);
513 products.should.have.length(4);
514 products[0].name.should.equal('Tool Z');
515 products[1].name.should.equal('Widget A');
516 products[2].name.should.equal('Widget B');
517 products[3].name.should.equal('Widget Z');
518 done();
519 });
520 });
521 });
522
523 describe('remove', function() {
524 const ids = {};
525
526 before(function(done) {
527 db.automigrate(setupProducts.bind(null, ids, done));
528 });
529
530 it('should apply default scope - custom where', function(done) {
531 Widget.remove({name: 'Widget A'}, function(err) {
532 should.not.exist(err);
533 Product.find(function(err, products) {
534 products.should.have.length(4);
535 products[0].name.should.equal('Tool A');
536 products[1].name.should.equal('Tool Z');
537 products[2].name.should.equal('Widget B');
538 products[3].name.should.equal('Widget Z');
539 done();
540 });
541 });
542 });
543
544 it('should apply default scope - custom where (no match)', function(done) {
545 Tool.remove({name: 'Widget Z'}, function(err) {
546 should.not.exist(err);
547 Product.find(function(err, products) {
548 products.should.have.length(4);
549 products[0].name.should.equal('Tool A');
550 products[1].name.should.equal('Tool Z');
551 products[2].name.should.equal('Widget B');
552 products[3].name.should.equal('Widget Z');
553 done();
554 });
555 });
556 });
557
558 it('should apply default scope - deleteAll', function(done) {
559 Tool.deleteAll(function(err) {
560 should.not.exist(err);
561 Product.find(function(err, products) {
562 products.should.have.length(2);
563 products[0].name.should.equal('Widget B');
564 products[1].name.should.equal('Widget Z');
565 done();
566 });
567 });
568 });
569
570 it('should create a scoped instance - tool', function(done) {
571 Tool.create({name: 'Tool B'}, function(err, p) {
572 should.not.exist(err);
573 Product.find(function(err, products) {
574 products.should.have.length(3);
575 products[0].name.should.equal('Tool B');
576 products[1].name.should.equal('Widget B');
577 products[2].name.should.equal('Widget Z');
578 done();
579 });
580 });
581 });
582
583 it('should apply default scope - destroyAll', function(done) {
584 Widget.destroyAll(function(err) {
585 should.not.exist(err);
586 Product.find(function(err, products) {
587 products.should.have.length(1);
588 products[0].name.should.equal('Tool B');
589 done();
590 });
591 });
592 });
593 });
594
595 describe('scopes', function() {
596 const ids = {};
597
598 before(function(done) {
599 db.automigrate(setupProducts.bind(null, ids, done));
600 });
601
602 it('should merge with default scope', function(done) {
603 Product.active(function(err, products) {
604 should.not.exist(err);
605 products.should.have.length(3);
606 products[0].name.should.equal('Tool Z');
607 products[1].name.should.equal('Widget A');
608 products[2].name.should.equal('Widget Z');
609 done();
610 });
611 });
612
613 it('should merge with default scope - tool', function(done) {
614 Tool.active(function(err, products) {
615 should.not.exist(err);
616 products.should.have.length(1);
617 products[0].name.should.equal('Tool Z');
618 done();
619 });
620 });
621
622 it('should merge with default scope - widget', function(done) {
623 Widget.active(function(err, products) {
624 should.not.exist(err);
625 products.should.have.length(2);
626 products[0].name.should.equal('Widget A');
627 products[1].name.should.equal('Widget Z');
628 done();
629 });
630 });
631 });
632
633 describe('scope function', function() {
634 before(function(done) {
635 db.automigrate(done);
636 });
637
638 it('should create a scoped instance - widget', function(done) {
639 Widget.create({name: 'Product', kind: 'ignored'}, function(err, p) {
640 p.name.should.equal('Product');
641 p.kind.should.equal('Widget');
642 done();
643 });
644 });
645
646 it('should create a scoped instance - thing', function(done) {
647 Thing.create({name: 'Product', kind: 'ignored'}, function(err, p) {
648 p.name.should.equal('Product');
649 p.kind.should.equal('Thing');
650 done();
651 });
652 });
653
654 it('should find a scoped instance - widget', function(done) {
655 Widget.findOne({where: {name: 'Product'}}, function(err, p) {
656 p.name.should.equal('Product');
657 p.kind.should.equal('Widget');
658 done();
659 });
660 });
661
662 it('should find a scoped instance - thing', function(done) {
663 Thing.findOne({where: {name: 'Product'}}, function(err, p) {
664 p.name.should.equal('Product');
665 p.kind.should.equal('Thing');
666 done();
667 });
668 });
669
670 // eslint-disable-next-line mocha/no-identical-title
671 it('should find a scoped instance - thing', function(done) {
672 Product.find({where: {name: 'Product'}}, function(err, products) {
673 products.should.have.length(2);
674 products[0].name.should.equal('Product');
675 products[1].name.should.equal('Product');
676 const kinds = products.map(function(p) { return p.kind; });
677 kinds.sort();
678 kinds.should.eql(['Thing', 'Widget']);
679 done();
680 });
681 });
682 });
683
684 describe('relations', function() {
685 const ids = {};
686
687 before(function(done) {
688 db.automigrate(done);
689 });
690
691 before(function(done) {
692 Category.create({name: 'Category A'}, function(err, cat) {
693 ids.categoryA = cat.id;
694 async.series([
695 function(next) {
696 cat.widgets.create({name: 'Widget B', kind: 'ignored'}, next);
697 },
698 function(next) {
699 cat.widgets.create({name: 'Widget A'}, next);
700 },
701 function(next) {
702 cat.tools.create({name: 'Tool A'}, next);
703 },
704 function(next) {
705 cat.things.create({name: 'Thing A'}, next);
706 },
707 ], done);
708 });
709 });
710
711 it('should apply default scope - products', function(done) {
712 Category.findById(ids.categoryA, function(err, cat) {
713 should.not.exist(err);
714 cat.products(function(err, products) {
715 should.not.exist(err);
716 products.should.have.length(4);
717 products[0].name.should.equal('Thing A');
718 products[1].name.should.equal('Tool A');
719 products[2].name.should.equal('Widget A');
720 products[3].name.should.equal('Widget B');
721
722 products[0].should.be.instanceof(Product);
723 products[0].should.be.instanceof(Thing);
724
725 products[1].should.be.instanceof(Product);
726 products[1].should.be.instanceof(Tool);
727
728 products[2].should.be.instanceof(Product);
729 products[2].should.be.instanceof(Widget);
730
731 done();
732 });
733 });
734 });
735
736 it('should apply default scope - widgets', function(done) {
737 Category.findById(ids.categoryA, function(err, cat) {
738 should.not.exist(err);
739 cat.widgets(function(err, products) {
740 should.not.exist(err);
741 products.should.have.length(2);
742 products[0].should.be.instanceof(Widget);
743 products[0].name.should.equal('Widget A');
744 products[1].name.should.equal('Widget B');
745 products[0].category(function(err, inst) {
746 inst.name.should.equal('Category A');
747 done();
748 });
749 });
750 });
751 });
752
753 it('should apply default scope - tools', function(done) {
754 Category.findById(ids.categoryA, function(err, cat) {
755 should.not.exist(err);
756 cat.tools(function(err, products) {
757 should.not.exist(err);
758 products.should.have.length(1);
759 products[0].should.be.instanceof(Tool);
760 products[0].name.should.equal('Tool A');
761 products[0].category(function(err, inst) {
762 inst.name.should.equal('Category A');
763 done();
764 });
765 });
766 });
767 });
768
769 it('should apply default scope - things', function(done) {
770 Category.findById(ids.categoryA, function(err, cat) {
771 should.not.exist(err);
772 cat.things(function(err, products) {
773 should.not.exist(err);
774 products.should.have.length(1);
775 products[0].should.be.instanceof(Thing);
776 products[0].name.should.equal('Thing A');
777 products[0].category(function(err, inst) {
778 inst.name.should.equal('Category A');
779 done();
780 });
781 });
782 });
783 });
784
785 it('should create related item with default scope', function(done) {
786 Category.findById(ids.categoryA, function(err, cat) {
787 cat.tools.create({name: 'Tool B'}, done);
788 });
789 });
790
791 it('should use relation scope order', function(done) {
792 Category.findById(ids.categoryA, function(err, cat) {
793 should.not.exist(err);
794 cat.tools(function(err, products) {
795 should.not.exist(err);
796 products.should.have.length(2);
797 products[0].name.should.equal('Tool B');
798 products[1].name.should.equal('Tool A');
799 done();
800 });
801 });
802 });
803 });
804
805 describe('with include option', function() {
806 before(function(done) {
807 db.automigrate(done);
808 });
809
810 before(function(done) {
811 Person.create({id: 1, name: 'Person A'}, function(err, person) {
812 person.things.create({name: 'Thing A'}, done);
813 });
814 });
815
816 it('should find a scoped instance with included relation - things', function(done) {
817 Person.findById(1, function(err, person) {
818 should.not.exist(err);
819 should.exist(person);
820 const things = person.things();
821 should.exist(things);
822 things.should.be.an.instanceOf(Array);
823 things.should.have.length(1);
824 done();
825 });
826 });
827 });
828});