loopback-datasource-juggler/test/model-builder.test.js

204 lines
7.1 KiB
JavaScript
Raw Normal View History

2019-05-08 15:45:37 +00:00
// Copyright IBM Corp. 2018,2019. All Rights Reserved.
// 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;
const {StrongGlobalize} = require('strong-globalize');
const parentRefHelper = require('./helpers/setup-parent-ref');
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');
});
});
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');
});
});
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);
});
});
function givenModelBuilderInstance() {
builder = new ModelBuilder();
}
});
});