2019-05-08 15:45:37 +00:00
|
|
|
// Copyright IBM Corp. 2018,2019. All Rights Reserved.
|
2018-01-08 08:25:31 +00:00
|
|
|
// Node module: loopback-datasource-juggler
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const should = require('./init.js');
|
|
|
|
|
|
|
|
const juggler = require('../');
|
2018-12-07 14:54:29 +00:00
|
|
|
const ModelBuilder = juggler.ModelBuilder;
|
2019-10-23 16:31:39 +00:00
|
|
|
const {StrongGlobalize} = require('strong-globalize');
|
|
|
|
const parentRefHelper = require('./helpers/setup-parent-ref');
|
2018-01-08 08:25:31 +00:00
|
|
|
|
|
|
|
describe('ModelBuilder', () => {
|
|
|
|
describe('define()', () => {
|
|
|
|
let builder;
|
|
|
|
|
|
|
|
beforeEach(givenModelBuilderInstance);
|
|
|
|
|
|
|
|
it('sets correct "modelName" property', () => {
|
|
|
|
const MyModel = builder.define('MyModel');
|
|
|
|
MyModel.should.have.property('modelName', 'MyModel');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sets correct "name" property on model constructor', () => {
|
|
|
|
const MyModel = builder.define('MyModel');
|
|
|
|
MyModel.should.have.property('name', 'MyModel');
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('model class name sanitization', () => {
|
|
|
|
it('converts "-" to "_"', () => {
|
|
|
|
const MyModel = builder.define('Grand-child');
|
|
|
|
MyModel.should.have.property('name', 'Grand_child');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('converts "." to "_"', () => {
|
|
|
|
const MyModel = builder.define('Grand.child');
|
|
|
|
MyModel.should.have.property('name', 'Grand_child');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('converts ":" to "_"', () => {
|
|
|
|
const MyModel = builder.define('local:User');
|
|
|
|
MyModel.should.have.property('name', 'local_User');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('falls back to legacy "ModelConstructor" in other cases', () => {
|
|
|
|
const MyModel = builder.define('Grand\tchild');
|
|
|
|
MyModel.should.have.property('name', 'ModelConstructor');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-01-25 08:46:31 +00:00
|
|
|
describe('model with nested properties as function', () => {
|
|
|
|
const Role = function(roleName) {};
|
|
|
|
it('sets correct nested properties', () => {
|
|
|
|
const User = builder.define('User', {
|
|
|
|
role: {
|
|
|
|
type: typeof Role,
|
|
|
|
default: null,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
should.equal(User.getPropertyType('role'), 'ModelConstructor');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('model with nested properties as class', () => {
|
|
|
|
class Role {
|
|
|
|
constructor(roleName) {}
|
|
|
|
}
|
|
|
|
it('sets correct nested properties', () => {
|
|
|
|
const User = builder.define('UserWithClass', {
|
|
|
|
role: {
|
|
|
|
type: Role,
|
|
|
|
default: null,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
User.registerProperty('role');
|
|
|
|
should.equal(User.getPropertyType('role'), 'Role');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-10-23 16:31:39 +00:00
|
|
|
describe('model with nested properties as embedded model', () => {
|
|
|
|
let Address, Person;
|
|
|
|
const originalWarn = StrongGlobalize.prototype.warn;
|
|
|
|
parentRefHelper(() => builder);
|
|
|
|
before('create stub for warning check', () => {
|
|
|
|
StrongGlobalize.prototype.warn = function gWarnWrapper(...args) {
|
|
|
|
StrongGlobalize.prototype.warn.called++;
|
|
|
|
return originalWarn.apply(this, args);
|
|
|
|
};
|
|
|
|
StrongGlobalize.prototype.warn.called = 0;
|
|
|
|
});
|
|
|
|
beforeEach('Define models', () => {
|
|
|
|
Address = builder.define('Address', {
|
|
|
|
street: {type: 'string'},
|
|
|
|
number: {type: 'number'},
|
|
|
|
});
|
|
|
|
Person = builder.define('Person', {
|
|
|
|
name: {type: 'string'},
|
|
|
|
address: {type: 'Address'},
|
|
|
|
other: {type: 'object'},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
after('restore warning stub', () => {
|
|
|
|
StrongGlobalize.prototype.warn = originalWarn;
|
|
|
|
});
|
|
|
|
it('should properly add the __parent relationship when instantiating parent model', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
address: {street: 'kopria', number: 11},
|
|
|
|
});
|
|
|
|
person.should.have.propertyByPath('address', '__parent').which.equals(person);
|
|
|
|
});
|
|
|
|
it('should add _parent property when setting embedded model after instantiation', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
});
|
|
|
|
person.address = {street: 'kopria', number: 11};
|
|
|
|
person.should.have.propertyByPath('address', '__parent').which.equals(person);
|
|
|
|
});
|
|
|
|
it('should handle nullish embedded property values', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
address: null,
|
|
|
|
});
|
|
|
|
person.should.have.property('address').which.equals(null);
|
|
|
|
});
|
|
|
|
it('should change __parent reference and WARN when moving a child instance to an other parent', () => {
|
|
|
|
const person1 = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
address: {street: 'kopria', number: 11},
|
|
|
|
});
|
|
|
|
const {address} = person1;
|
|
|
|
address.should.be.instanceof(Address).and.have.property('__parent').which.equals(person1);
|
|
|
|
StrongGlobalize.prototype.warn.should.have.property('called', 0); // check that no warn yet
|
|
|
|
const person2 = new Person({
|
|
|
|
name: 'Allos',
|
|
|
|
address,
|
|
|
|
});
|
|
|
|
address.should.have.property('__parent').which.equals(person2);
|
|
|
|
StrongGlobalize.prototype.warn.should.have.property('called', 1); // check we had a warning
|
|
|
|
});
|
|
|
|
it('should NOT provide the __parent property to any serialization of the instance', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
address: {street: 'kopria', number: 11},
|
|
|
|
});
|
|
|
|
person.toJSON().should.not.have.propertyByPath('address', '__parent');
|
|
|
|
person.toObject().should.not.have.propertyByPath('address', '__parent');
|
|
|
|
});
|
|
|
|
it('should NOT provide __parent property in plain object properties', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'Mitsos',
|
|
|
|
address: {street: 'kopria', number: 11},
|
|
|
|
other: {some: 'object'},
|
|
|
|
});
|
|
|
|
person.should.have.property('other').which.eql({some: 'object'}).and.not.has
|
|
|
|
.property('__parent');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Model with properties as list of embedded models', () => {
|
|
|
|
let Person, Address;
|
|
|
|
beforeEach('Define models', () => {
|
|
|
|
Address = builder.define('Address', {
|
|
|
|
street: {type: 'string'},
|
|
|
|
number: {type: 'number'},
|
|
|
|
});
|
|
|
|
Person = builder.define('Person', {
|
|
|
|
name: {type: 'string'},
|
|
|
|
addresses: {type: ['Address']}, // array of addresses
|
|
|
|
});
|
|
|
|
});
|
|
|
|
it('should pass the container model instance as parent to the list item', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'mitsos',
|
|
|
|
addresses: [{
|
|
|
|
street: 'kapou oraia',
|
|
|
|
number: 100,
|
|
|
|
}],
|
|
|
|
});
|
|
|
|
person.should.have.property('addresses').which.has.property('parent')
|
|
|
|
.which.is.instanceof(Person).and.equals(person);
|
|
|
|
});
|
|
|
|
it('should pass the container model instance as parent to the list, when assigning to ' +
|
|
|
|
'the list property', () => {
|
|
|
|
const person = new Person({
|
|
|
|
name: 'mitsos',
|
|
|
|
});
|
|
|
|
person.addresses = [{
|
|
|
|
street: 'kapou oraia',
|
|
|
|
number: 100,
|
|
|
|
}];
|
|
|
|
person.should.have.property('addresses').which.has.property('parent')
|
|
|
|
.which.is.instanceof(Person).and.equals(person);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-01-08 08:25:31 +00:00
|
|
|
function givenModelBuilderInstance() {
|
|
|
|
builder = new ModelBuilder();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|