2019-05-08 15:45:37 +00:00
|
|
|
// Copyright IBM Corp. 2013,2018. All Rights Reserved.
|
2016-04-01 22:25:16 +00:00
|
|
|
// Node module: loopback-datasource-juggler
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2013-05-28 20:50:59 +00:00
|
|
|
// This test written in mocha+should.js
|
2016-08-22 19:55:22 +00:00
|
|
|
'use strict';
|
2018-12-07 14:54:29 +00:00
|
|
|
const should = require('./init.js');
|
|
|
|
const assert = require('assert');
|
|
|
|
const async = require('async');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const jdb = require('../');
|
|
|
|
const ModelBuilder = jdb.ModelBuilder;
|
|
|
|
const DataSource = jdb.DataSource;
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('ModelBuilder', function() {
|
|
|
|
it('supports plain models', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {
|
2014-01-24 17:09:53 +00:00
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
2016-04-01 11:48:17 +00:00
|
|
|
age: Number,
|
2013-07-25 05:54:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// define any custom method
|
2016-04-01 11:48:17 +00:00
|
|
|
User.prototype.getNameAndAge = function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2015-02-03 10:37:43 +00:00
|
|
|
modelBuilder.models.should.be.type('object').and.have.property('User').exactly(User);
|
|
|
|
modelBuilder.definitions.should.be.type('object').and.have.property('User');
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20, xyz: false});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
2014-08-21 20:47:09 +00:00
|
|
|
user.should.have.property('xyz', false);
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.have.property('bio', undefined);
|
2014-01-24 17:09:53 +00:00
|
|
|
done(null, User);
|
|
|
|
});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('ignores unknown properties in strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object');
|
2014-02-04 04:52:01 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.not.have.property('age');
|
|
|
|
user.toObject().should.not.have.property('age');
|
|
|
|
user.toObject(true).should.not.have.property('age');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.have.property('bio', undefined);
|
2014-01-24 17:09:53 +00:00
|
|
|
done(null, User);
|
|
|
|
});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('ignores non-predefined properties in strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2014-01-30 19:51:34 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, bio: String}, {strict: true});
|
2014-01-30 19:51:34 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe'});
|
2014-01-30 19:51:34 +00:00
|
|
|
user.age = 10;
|
|
|
|
user.bio = 'me';
|
|
|
|
|
2014-02-04 04:52:01 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('bio', 'me');
|
|
|
|
|
|
|
|
// Non predefined property age should be ignored in strict mode if schemaOnly parameter is not false
|
|
|
|
user.toObject().should.not.have.property('age');
|
|
|
|
user.toObject(true).should.not.have.property('age');
|
|
|
|
user.toObject(false).should.have.property('age', 10);
|
|
|
|
|
|
|
|
// Predefined property bio should be kept in strict mode
|
|
|
|
user.toObject().should.have.property('bio', 'me');
|
|
|
|
user.toObject(true).should.have.property('bio', 'me');
|
|
|
|
user.toObject(false).should.have.property('bio', 'me');
|
2014-01-30 19:51:34 +00:00
|
|
|
done(null, User);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error when unknown properties are used if strict=throw', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, bio: String}, {strict: 'throw'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(false, 'The code should have thrown an error');
|
|
|
|
} catch (e) {
|
|
|
|
assert(true, 'The code is expected to throw an error');
|
|
|
|
}
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('supports open models', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {}, {strict: false});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('accepts non-predefined properties in non-strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2014-01-30 19:51:34 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, bio: String}, {strict: false});
|
2014-01-30 19:51:34 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe'});
|
2014-01-30 19:51:34 +00:00
|
|
|
user.age = 10;
|
|
|
|
user.bio = 'me';
|
|
|
|
|
2014-02-04 04:52:01 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('bio', 'me');
|
|
|
|
|
|
|
|
// Non predefined property age should be kept in non-strict mode
|
|
|
|
user.toObject().should.have.property('age', 10);
|
|
|
|
user.toObject(true).should.have.property('age', 10);
|
|
|
|
user.toObject(false).should.have.property('age', 10);
|
|
|
|
|
|
|
|
// Predefined property bio should be kept
|
|
|
|
user.toObject().should.have.property('bio', 'me');
|
2018-05-10 17:13:14 +00:00
|
|
|
user.toObject({onlySchema: true}).should.have.property('bio', 'me');
|
|
|
|
user.toObject({onlySchema: false}).should.have.property('bio', 'me');
|
2014-02-04 04:52:01 +00:00
|
|
|
|
2014-01-30 19:51:34 +00:00
|
|
|
done(null, User);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses non-strict mode by default', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {});
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('supports nested model definitions', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// simplier way to describe model
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {
|
2014-01-24 17:09:53 +00:00
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
|
|
|
age: Number,
|
|
|
|
address: {
|
|
|
|
street: String,
|
|
|
|
city: String,
|
|
|
|
state: String,
|
|
|
|
zipCode: String,
|
2016-04-01 11:48:17 +00:00
|
|
|
country: String,
|
2014-01-24 17:09:53 +00:00
|
|
|
},
|
|
|
|
emails: [
|
|
|
|
{
|
|
|
|
label: String,
|
2016-04-01 11:48:17 +00:00
|
|
|
email: String,
|
|
|
|
},
|
2014-01-24 17:09:53 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
friends: [String],
|
2013-07-11 21:24:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// define any custom method
|
2016-04-01 11:48:17 +00:00
|
|
|
User.prototype.getNameAndAge = function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2015-02-03 10:37:43 +00:00
|
|
|
modelBuilder.models.should.be.type('object').and.have.property('User', User);
|
|
|
|
modelBuilder.definitions.should.be.type('object').and.have.property('User');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let user = new User({
|
2014-01-24 17:09:53 +00:00
|
|
|
name: 'Joe', age: 20,
|
2016-08-19 17:46:59 +00:00
|
|
|
address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
2014-01-24 17:09:53 +00:00
|
|
|
emails: [
|
2016-08-19 17:46:59 +00:00
|
|
|
{label: 'work', email: 'xyz@sample.com'},
|
2014-01-24 17:09:53 +00:00
|
|
|
],
|
2016-04-01 11:48:17 +00:00
|
|
|
friends: ['Mary', 'John'],
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.have.property('bio', undefined);
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('address');
|
|
|
|
user.address.should.have.property('city', 'San Jose');
|
|
|
|
user.address.should.have.property('state', 'CA');
|
|
|
|
|
|
|
|
user = user.toObject();
|
|
|
|
user.emails.should.have.property('length', 1);
|
|
|
|
user.emails[0].should.have.property('label', 'work');
|
|
|
|
user.emails[0].should.have.property('email', 'xyz@sample.com');
|
|
|
|
user.friends.should.have.property('length', 2);
|
|
|
|
assert.equal(user.friends[0], 'Mary');
|
|
|
|
assert.equal(user.friends[1], 'John');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('allows models to be referenced by name before they are defined', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, address: 'Address'});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let user;
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2016-08-19 17:46:59 +00:00
|
|
|
user = new User({name: 'Joe', address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'}});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(false, 'An exception should have been thrown');
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Address = modelBuilder.define('Address', {
|
2014-01-24 17:09:53 +00:00
|
|
|
street: String,
|
|
|
|
city: String,
|
|
|
|
state: String,
|
|
|
|
zipCode: String,
|
2016-04-01 11:48:17 +00:00
|
|
|
country: String,
|
2013-11-05 06:53:02 +00:00
|
|
|
});
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
user = new User({name: 'Joe', address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'}});
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
|
|
|
User.definition.properties.address.should.have.property('type', Address);
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object');
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(user.name === 'Joe');
|
|
|
|
user.address.should.have.property('city', 'San Jose');
|
|
|
|
user.address.should.have.property('state', 'CA');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('defines an id property for composite ids', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
|
|
|
const Follow = modelBuilder.define('Follow', {
|
2016-08-19 17:46:59 +00:00
|
|
|
followerId: {type: String, id: 1},
|
|
|
|
followeeId: {type: String, id: 2},
|
2016-04-01 11:48:17 +00:00
|
|
|
followAt: Date,
|
2015-01-22 02:39:47 +00:00
|
|
|
});
|
2018-12-07 14:54:29 +00:00
|
|
|
const follow = new Follow({followerId: 1, followeeId: 2});
|
2015-01-22 02:39:47 +00:00
|
|
|
|
|
|
|
follow.should.have.property('id');
|
2016-08-19 17:46:59 +00:00
|
|
|
assert.deepEqual(follow.id, {followerId: 1, followeeId: 2});
|
2015-01-22 02:39:47 +00:00
|
|
|
});
|
2017-04-04 17:35:24 +00:00
|
|
|
|
|
|
|
it('instantiates model from data with no constructor', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2017-04-04 17:35:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, age: Number});
|
2017-04-04 17:35:24 +00:00
|
|
|
|
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const data = Object.create(null);
|
2017-04-04 17:35:24 +00:00
|
|
|
data.name = 'Joe';
|
|
|
|
data.age = 20;
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User(data);
|
2017-04-04 17:35:24 +00:00
|
|
|
assert(true, 'The code is expected to pass');
|
|
|
|
} catch (e) {
|
|
|
|
assert(false, 'The code should have not thrown an error');
|
|
|
|
}
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('instantiates model from data with non function constructor', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2017-04-04 17:35:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {name: String, age: Number});
|
2017-04-04 17:35:24 +00:00
|
|
|
|
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const Person = function(name, age) {
|
2017-04-04 17:35:24 +00:00
|
|
|
this.name = name;
|
|
|
|
this.age = age;
|
|
|
|
};
|
|
|
|
|
|
|
|
Person.prototype.constructor = 'constructor';
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const data = new Person('Joe', 20);
|
2017-04-04 17:35:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User(data);
|
2017-04-04 17:35:24 +00:00
|
|
|
assert(false, 'The code should have thrown an error');
|
|
|
|
} catch (e) {
|
|
|
|
e.message.should.equal('Property name "constructor" is not allowed in User data');
|
|
|
|
assert(true, 'The code is expected to throw an error');
|
|
|
|
}
|
|
|
|
done();
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-08-27 16:14:17 +00:00
|
|
|
describe('DataSource ping', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-08-27 16:14:17 +00:00
|
|
|
ds.settings.connectionTimeout = 50; // ms
|
|
|
|
ds.connector.connect = function(cb) {
|
|
|
|
// Mock up the long delay
|
|
|
|
setTimeout(cb, 100);
|
|
|
|
};
|
|
|
|
ds.connector.ping = function(cb) {
|
|
|
|
cb(new Error('bad connection 2'));
|
2016-04-01 11:48:17 +00:00
|
|
|
};
|
2014-08-27 16:14:17 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('reports connection errors during ping', function(done) {
|
2014-08-27 16:14:17 +00:00
|
|
|
ds.ping(function(err) {
|
|
|
|
(!!err).should.be.true;
|
|
|
|
err.message.should.be.eql('bad connection 2');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('cancels invocation after timeout', function(done) {
|
2014-08-27 16:14:17 +00:00
|
|
|
ds.connected = false; // Force connect
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {
|
2016-08-19 17:46:59 +00:00
|
|
|
title: {type: String, length: 255},
|
2014-08-27 16:14:17 +00:00
|
|
|
});
|
|
|
|
Post.create(function(err) {
|
|
|
|
(!!err).should.be.true;
|
|
|
|
err.message.should.be.eql('Timeout in connecting after 50 ms');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('DataSource define model', function() {
|
2016-10-27 20:36:59 +00:00
|
|
|
it('supports plain model definitions', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
// define models
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {
|
2016-08-19 17:46:59 +00:00
|
|
|
title: {type: String, length: 255},
|
|
|
|
content: {type: ModelBuilder.Text},
|
|
|
|
date: {type: Date, default: function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return new Date();
|
2016-08-19 17:46:59 +00:00
|
|
|
}},
|
|
|
|
timestamp: {type: Number, default: Date.now},
|
|
|
|
published: {type: Boolean, default: false, index: true},
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
// simpler way to describe model
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {
|
2014-01-24 17:09:53 +00:00
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
2016-08-19 17:46:59 +00:00
|
|
|
joinedAt: {type: Date, default: Date},
|
2016-04-01 11:48:17 +00:00
|
|
|
age: Number,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Group = ds.define('Group', {group: String});
|
2014-01-24 17:09:53 +00:00
|
|
|
User.mixin(Group);
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
// define any custom method
|
2016-04-01 11:48:17 +00:00
|
|
|
User.prototype.getNameAndAge = function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', group: 'G1'});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(user.name, 'Joe');
|
|
|
|
assert.equal(user.group, 'G1');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-06-21 19:53:06 +00:00
|
|
|
assert(user.joinedAt instanceof Date);
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// setup relationships
|
2016-08-19 17:46:59 +00:00
|
|
|
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.hasAndBelongsToMany('groups');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user2 = new User({name: 'Smith'});
|
2016-04-01 11:48:17 +00:00
|
|
|
user2.save(function(err) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const post = user2.posts.build({title: 'Hello world'});
|
2016-04-01 11:48:17 +00:00
|
|
|
post.save(function(err, data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
// console.log(err ? err : data);
|
|
|
|
});
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Post.findOne({where: {published: false}, order: 'date DESC'}, function(err, data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
// console.log(data);
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.create({name: 'Jeff'}, function(err, data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (err) {
|
|
|
|
return;
|
|
|
|
}
|
2018-12-07 14:54:29 +00:00
|
|
|
const post = data.posts.build({title: 'My Post'});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.create({name: 'Ray'}, function(err, data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
// console.log(data);
|
|
|
|
});
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Article = ds.define('Article', {title: String});
|
|
|
|
const Tag = ds.define('Tag', {name: String});
|
2014-01-24 17:09:53 +00:00
|
|
|
Article.hasAndBelongsToMany('tags');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
Article.create(function(e, article) {
|
2016-08-19 17:46:59 +00:00
|
|
|
article.tags.create({name: 'popular'}, function(err, data) {
|
2016-04-01 11:48:17 +00:00
|
|
|
Article.findOne(function(e, article) {
|
|
|
|
article.tags(function(e, tags) {
|
2014-01-24 17:09:53 +00:00
|
|
|
// console.log(tags);
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
// should be able to attach a data source to an existing model
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Color = modelBuilder.define('Color', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Color.should.not.have.property('create');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
// attach
|
2014-01-24 17:09:53 +00:00
|
|
|
ds.attach(Color);
|
|
|
|
Color.should.have.property('create');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Color.create({name: 'red'});
|
|
|
|
Color.create({name: 'green'});
|
|
|
|
Color.create({name: 'blue'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
Color.all(function(err, colors) {
|
2014-01-24 17:09:53 +00:00
|
|
|
colors.should.have.lengthOf(3);
|
2013-05-28 20:50:59 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('emits events during attach', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const modelBuilder = new ModelBuilder();
|
2014-08-01 15:22:33 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2014-08-01 15:22:33 +00:00
|
|
|
});
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
let seq = 0;
|
|
|
|
let dataAccessConfigured = -1;
|
|
|
|
let dataSourceAttached = -1;
|
2014-08-01 15:22:33 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
User.on('dataAccessConfigured', function(model) {
|
2014-08-01 15:22:33 +00:00
|
|
|
dataAccessConfigured = seq++;
|
|
|
|
assert(User.create);
|
|
|
|
assert(User.hasMany);
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
User.on('dataSourceAttached', function(model) {
|
2014-08-01 15:22:33 +00:00
|
|
|
assert(User.dataSource instanceof DataSource);
|
|
|
|
dataSourceAttached = seq++;
|
|
|
|
});
|
|
|
|
|
|
|
|
ds.attach(User);
|
|
|
|
assert.equal(dataAccessConfigured, 0);
|
|
|
|
assert.equal(dataSourceAttached, 1);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('ignores unknown properties in strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function(err, user) {
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object');
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(user.name === 'Joe');
|
|
|
|
assert(user.age === undefined);
|
|
|
|
assert(user.toObject().age === undefined);
|
|
|
|
assert(user.toObject(true).age === undefined);
|
|
|
|
assert(user.bio === undefined);
|
|
|
|
done(null, User);
|
2013-08-26 20:38:24 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error when unknown properties are used if strict=throw', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {strict: 'throw'});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(false, 'The code should have thrown an error');
|
|
|
|
} catch (e) {
|
|
|
|
assert(true, 'The code is expected to throw an error');
|
|
|
|
}
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2015-04-09 14:57:38 +00:00
|
|
|
describe('strict mode "validate"', function() {
|
2016-10-27 20:36:59 +00:00
|
|
|
it('reports validation errors for unknown properties', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const User = ds.define('User', {name: String}, {strict: 'validate'});
|
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2015-04-09 14:57:38 +00:00
|
|
|
user.isValid().should.be.false;
|
2018-12-07 14:54:29 +00:00
|
|
|
const codes = user.errors && user.errors.codes || {};
|
2015-04-09 14:57:38 +00:00
|
|
|
codes.should.have.property('age').eql(['unknown-property']);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('supports open model definitions', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {}, {strict: false});
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function(err, user) {
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
2013-08-29 04:49:05 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
User.findById(user.id, function(err, user) {
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses non-strict mode by default', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function(err, user) {
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object').and.have.property('name', 'Joe');
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
done(null, User);
|
2013-08-26 20:38:24 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses strict mode by default for relational DBs', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-01-24 17:09:53 +00:00
|
|
|
ds.connector.relational = true; // HACK
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object');
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(user.name === 'Joe');
|
|
|
|
assert(user.age === undefined);
|
|
|
|
assert(user.toObject().age === undefined);
|
|
|
|
assert(user.toObject(true).age === undefined);
|
|
|
|
assert(user.bio === undefined);
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error with unknown properties in non-strict mode for relational DBs', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-01-24 17:09:53 +00:00
|
|
|
ds.connector.relational = true; // HACK
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {strict: 'throw'});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(false, 'The code should have thrown an error');
|
|
|
|
} catch (e) {
|
|
|
|
assert(true, 'The code is expected to throw an error');
|
|
|
|
}
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-09-18 23:34:52 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('changes the property value for save in non-strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');// define models
|
|
|
|
const Post = ds.define('Post');
|
2013-09-18 23:34:52 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Post.create({price: 900}, function(err, post) {
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(post.price, 900);
|
|
|
|
post.price = 1000;
|
2016-04-01 11:48:17 +00:00
|
|
|
post.save(function(err, result) {
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(1000, result.price);
|
|
|
|
done(err, result);
|
|
|
|
});
|
2013-09-18 23:34:52 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-09-18 23:34:52 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('supports instance level strict mode', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-02-11 06:46:25 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {strict: true});
|
2014-02-11 06:46:25 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const user = new User({name: 'Joe', age: 20}, {strict: false});
|
2014-02-11 06:46:25 +00:00
|
|
|
|
|
|
|
user.should.have.property('__strict', false);
|
2015-02-03 10:37:43 +00:00
|
|
|
user.should.be.type('object');
|
2014-02-11 06:46:25 +00:00
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.toObject().should.have.property('age', 20);
|
|
|
|
user.toObject(true).should.have.property('age', 20);
|
|
|
|
|
|
|
|
user.setStrict(true);
|
|
|
|
user.toObject().should.not.have.property('age');
|
|
|
|
user.toObject(true).should.not.have.property('age');
|
|
|
|
user.toObject(false).should.have.property('age', 20);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('updates instances with unknown properties in non-strict mode', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');// define models
|
2018-12-07 15:22:36 +00:00
|
|
|
const Post = ds.define('Post', {
|
2016-08-19 17:46:59 +00:00
|
|
|
title: {type: String, length: 255, index: true},
|
|
|
|
content: {type: String},
|
2014-02-25 02:38:45 +00:00
|
|
|
});
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
Post.create({title: 'a', content: 'AAA'}, function(err, post) {
|
|
|
|
post.updateAttributes({title: 'b', xyz: 'xyz'}, function(err, p) {
|
2014-02-25 02:38:45 +00:00
|
|
|
should.not.exist(err);
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.xyz.should.be.equal('xyz');
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
Post.findById(post.id, function(err, p) {
|
2014-02-25 02:38:45 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.xyz.should.be.equal('xyz');
|
|
|
|
p.title.should.be.equal('b');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('injects id by default', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2014-01-24 22:51:01 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {});
|
2014-01-24 22:51:01 +00:00
|
|
|
assert.deepEqual(User.definition.properties.id,
|
2017-08-22 20:09:45 +00:00
|
|
|
{type: Number, id: 1, generated: true, updateOnly: true});
|
2014-01-24 22:51:01 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('disables idInjection if the value is false', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2014-01-24 22:51:01 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User1 = ds.define('User', {}, {idInjection: false});
|
2014-01-24 22:51:01 +00:00
|
|
|
assert(!User1.definition.properties.id);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
it('updates generated id type by the connector', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
2014-01-24 22:51:01 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = builder.define('User', {id: {type: String, generated: true, id: true}});
|
2014-01-24 22:51:01 +00:00
|
|
|
assert.deepEqual(User.definition.properties.id,
|
2017-08-22 20:09:45 +00:00
|
|
|
{type: String, id: 1, generated: true, updateOnly: true});
|
2014-01-24 22:51:01 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');// define models
|
2014-01-24 22:51:01 +00:00
|
|
|
User.attachTo(ds);
|
|
|
|
|
|
|
|
assert.deepEqual(User.definition.properties.id,
|
2017-08-22 20:09:45 +00:00
|
|
|
{type: Number, id: 1, generated: true, updateOnly: true});
|
2014-01-24 22:51:01 +00:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('allows an explicit remoting path', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
http: {path: 'accounts'},
|
2014-08-04 17:45:47 +00:00
|
|
|
});
|
2014-08-01 07:38:25 +00:00
|
|
|
User.http.path.should.equal('/accounts');
|
|
|
|
});
|
2014-01-24 22:51:01 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('allows an explicit remoting path with leading /', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2015-04-24 15:23:13 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, bio: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
http: {path: '/accounts'},
|
2015-04-24 15:23:13 +00:00
|
|
|
});
|
|
|
|
User.http.path.should.equal('/accounts');
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('Model loaded with a base', function() {
|
|
|
|
it('has a base class according to the base option', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2013-11-08 01:11:17 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String});
|
2013-11-08 17:02:17 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.staticMethod = function staticMethod() {
|
|
|
|
};
|
|
|
|
User.prototype.instanceMethod = function instanceMethod() {
|
|
|
|
};
|
2013-11-08 17:02:17 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = ds.define('Customer', {vip: Boolean}, {base: 'User'});
|
2013-11-08 01:11:17 +00:00
|
|
|
|
|
|
|
assert(Customer.prototype instanceof User);
|
2013-11-08 17:02:17 +00:00
|
|
|
assert(Customer.staticMethod === User.staticMethod);
|
|
|
|
assert(Customer.prototype.instanceMethod === User.prototype.instanceMethod);
|
2014-07-21 17:21:30 +00:00
|
|
|
assert.equal(Customer.base, User);
|
|
|
|
assert.equal(Customer.base, Customer.super_);
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2013-11-08 01:11:17 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer1 = ds.define('Customer1', {vip: Boolean}, {base: 'User1'});
|
2014-01-24 17:09:53 +00:00
|
|
|
} catch (e) {
|
2013-11-08 01:11:17 +00:00
|
|
|
assert(e);
|
|
|
|
}
|
2014-07-21 17:21:30 +00:00
|
|
|
});
|
2013-11-08 01:11:17 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('inherits properties from base model', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2015-02-07 19:15:28 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String});
|
2015-02-07 19:15:28 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = ds.define('Customer', {vip: Boolean}, {base: 'User'});
|
2015-02-07 19:15:28 +00:00
|
|
|
|
|
|
|
Customer.definition.properties.should.have.property('name');
|
|
|
|
Customer.definition.properties.name.should.have.property('type', String);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('inherits properties by clone from base model', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2015-07-10 17:03:51 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String});
|
2015-07-10 17:03:51 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer1 = ds.define('Customer1', {vip: Boolean}, {base: 'User'});
|
|
|
|
const Customer2 = ds.define('Customer2', {vip: Boolean}, {base: 'User'});
|
2015-07-10 17:03:51 +00:00
|
|
|
|
|
|
|
Customer1.definition.properties.should.have.property('name');
|
|
|
|
Customer2.definition.properties.should.have.property('name');
|
|
|
|
Customer1.definition.properties.name.should.not.be.equal(
|
2018-07-16 06:46:25 +00:00
|
|
|
Customer2.definition.properties.name
|
|
|
|
);
|
2015-07-10 17:03:51 +00:00
|
|
|
Customer1.definition.properties.name.should.eql(
|
2018-07-16 06:46:25 +00:00
|
|
|
Customer2.definition.properties.name
|
|
|
|
);
|
2015-07-10 17:03:51 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('can remove properties from base model', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2015-03-03 18:27:22 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {username: String, email: String});
|
2015-03-03 18:27:22 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = ds.define('Customer',
|
2016-08-19 17:46:59 +00:00
|
|
|
{name: String, username: null, email: false},
|
|
|
|
{base: 'User'});
|
2015-03-03 18:27:22 +00:00
|
|
|
|
|
|
|
Customer.definition.properties.should.have.property('name');
|
|
|
|
// username/email are now shielded
|
|
|
|
Customer.definition.properties.should.not.have.property('username');
|
|
|
|
Customer.definition.properties.should.not.have.property('email');
|
2018-12-07 14:54:29 +00:00
|
|
|
const c = new Customer({name: 'John'});
|
2015-03-03 18:27:22 +00:00
|
|
|
c.should.have.property('username', undefined);
|
|
|
|
c.should.have.property('email', undefined);
|
|
|
|
c.should.have.property('name', 'John');
|
2018-12-07 14:54:29 +00:00
|
|
|
const u = new User({username: 'X', email: 'x@y.com'});
|
2015-03-03 18:27:22 +00:00
|
|
|
u.should.not.have.property('name');
|
|
|
|
u.should.have.property('username', 'X');
|
|
|
|
u.should.have.property('email', 'x@y.com');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('can configure base class via parent argument', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new ModelBuilder();
|
2014-07-21 17:21:30 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String});
|
2014-07-21 17:21:30 +00:00
|
|
|
|
|
|
|
User.staticMethod = function staticMethod() {
|
|
|
|
};
|
|
|
|
User.prototype.instanceMethod = function instanceMethod() {
|
|
|
|
};
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = ds.define('Customer', {vip: Boolean}, {}, User);
|
2014-07-21 17:21:30 +00:00
|
|
|
|
2015-02-07 19:15:28 +00:00
|
|
|
Customer.definition.properties.should.have.property('name');
|
|
|
|
Customer.definition.properties.name.should.have.property('type', String);
|
|
|
|
|
2014-07-21 17:21:30 +00:00
|
|
|
assert(Customer.prototype instanceof User);
|
|
|
|
assert(Customer.staticMethod === User.staticMethod);
|
|
|
|
assert(Customer.prototype.instanceMethod === User.prototype.instanceMethod);
|
|
|
|
assert.equal(Customer.base, User);
|
|
|
|
assert.equal(Customer.base, Customer.super_);
|
2013-11-08 01:11:17 +00:00
|
|
|
});
|
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-05-09 22:27:45 +00:00
|
|
|
describe('Models attached to a dataSource', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
let Post;
|
2014-05-09 22:27:45 +00:00
|
|
|
before(function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');// define models
|
2014-05-09 22:27:45 +00:00
|
|
|
Post = ds.define('Post', {
|
2016-08-19 17:46:59 +00:00
|
|
|
title: {type: String, length: 255, index: true},
|
|
|
|
content: {type: String},
|
2016-04-01 11:48:17 +00:00
|
|
|
comments: [String],
|
2016-08-19 17:46:59 +00:00
|
|
|
}, {forceId: false});
|
2014-05-09 22:27:45 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(function(done) {
|
|
|
|
Post.destroyAll(done);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('updateOrCreate', function() {
|
|
|
|
it('updates instances', function(done) {
|
|
|
|
Post.create({title: 'a', content: 'AAA'}, function(err, post) {
|
|
|
|
post.title = 'b';
|
|
|
|
Post.updateOrCreate(post, function(err, p) {
|
|
|
|
should.not.exist(err);
|
2014-05-09 22:27:45 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
should.not.exist(p._id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(post.id, function(err, p) {
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.title.should.be.equal('b');
|
|
|
|
done();
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
});
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('updates instances without removing existing properties', function(done) {
|
|
|
|
Post.create({title: 'a', content: 'AAA', comments: ['Comment1']}, function(err, post) {
|
|
|
|
post = post.toObject();
|
|
|
|
delete post.title;
|
|
|
|
delete post.comments;
|
|
|
|
Post.updateOrCreate(post, function(err, p) {
|
|
|
|
should.not.exist(err);
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
should.not.exist(p._id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(post.id, function(err, p) {
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.title.should.be.equal('a');
|
|
|
|
p.comments.length.should.be.equal(1);
|
|
|
|
p.comments[0].should.be.equal('Comment1');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('creates a new instance if it does not exist', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const post = {id: 123, title: 'a', content: 'AAA'};
|
2016-04-01 11:48:17 +00:00
|
|
|
Post.updateOrCreate(post, function(err, p) {
|
2014-05-09 22:27:45 +00:00
|
|
|
should.not.exist(err);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.title.should.be.equal(post.title);
|
2014-05-09 22:27:45 +00:00
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(p.id, function(err, p) {
|
2014-05-09 22:27:45 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.title.should.be.equal(post.title);
|
|
|
|
p.id.should.be.equal(post.id);
|
2014-05-09 22:27:45 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('save', function() {
|
|
|
|
it('updates instance with the same id', function(done) {
|
|
|
|
Post.create({title: 'a', content: 'AAA'}, function(err, post) {
|
|
|
|
post.title = 'b';
|
|
|
|
post.save(function(err, p) {
|
|
|
|
should.not.exist(err);
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
should.not.exist(p._id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(post.id, function(err, p) {
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.title.should.be.equal('b');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('updates the instance without removing existing properties', function(done) {
|
|
|
|
Post.create({title: 'a', content: 'AAA'}, function(err, post) {
|
|
|
|
delete post.title;
|
|
|
|
post.save(function(err, p) {
|
|
|
|
should.not.exist(err);
|
2014-05-09 22:27:45 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
should.not.exist(p._id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(post.id, function(err, p) {
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.title.should.be.equal('a');
|
|
|
|
done();
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('creates a new instance if it does not exist', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const post = new Post({id: '123', title: 'a', content: 'AAA'});
|
2016-10-27 20:36:59 +00:00
|
|
|
post.save(post, function(err, p) {
|
2014-05-09 22:27:45 +00:00
|
|
|
should.not.exist(err);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.title.should.be.equal(post.title);
|
2014-05-09 22:27:45 +00:00
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
2014-05-09 22:27:45 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
Post.findById(p.id, function(err, p) {
|
2014-05-09 22:27:45 +00:00
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
should.not.exist(p._id);
|
|
|
|
p.content.should.be.equal(post.content);
|
2016-10-27 20:36:59 +00:00
|
|
|
p.title.should.be.equal(post.title);
|
|
|
|
p.id.should.be.equal(post.id);
|
2014-05-09 22:27:45 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-01-28 22:23:48 +00:00
|
|
|
describe('DataSource connector types', function() {
|
2016-10-27 20:36:59 +00:00
|
|
|
it('returns an array of types using getTypes', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const types = ds.getTypes();
|
2014-01-28 22:23:48 +00:00
|
|
|
assert.deepEqual(types, ['db', 'nosql', 'memory']);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('supportTypes', function() {
|
|
|
|
it('tests supported types by string', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const result = ds.supportTypes('db');
|
2016-10-27 20:36:59 +00:00
|
|
|
assert(result);
|
|
|
|
});
|
2014-01-28 22:23:48 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('tests supported types by array', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const result = ds.supportTypes(['db', 'memory']);
|
2016-10-27 20:36:59 +00:00
|
|
|
assert(result);
|
|
|
|
});
|
2014-01-28 22:23:48 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('tests unsupported types by string', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const result = ds.supportTypes('rdbms');
|
2016-10-27 20:36:59 +00:00
|
|
|
assert(!result);
|
|
|
|
});
|
2014-01-28 22:23:48 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('tests unsupported types by array', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
let result = ds.supportTypes(['rdbms', 'memory']);
|
2016-10-27 20:36:59 +00:00
|
|
|
assert(!result);
|
2014-01-28 22:23:48 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
result = ds.supportTypes(['rdbms']);
|
|
|
|
assert(!result);
|
|
|
|
});
|
2014-01-28 22:23:48 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-06-12 07:13:32 +00:00
|
|
|
describe('DataSource._resolveConnector', function() {
|
2014-01-13 19:06:02 +00:00
|
|
|
// Mocked require
|
2018-12-07 14:54:29 +00:00
|
|
|
const loader = function(name) {
|
2014-03-13 05:11:55 +00:00
|
|
|
if (name.indexOf('./connectors/') !== -1) {
|
|
|
|
// ./connectors/<name> doesn't exist
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (name === 'loopback-connector-abc') {
|
|
|
|
// Assume loopback-connector-abc doesn't exist
|
|
|
|
return null;
|
|
|
|
}
|
2014-01-13 19:06:02 +00:00
|
|
|
return {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: name,
|
2014-03-13 05:11:55 +00:00
|
|
|
};
|
2014-01-13 19:06:02 +00:00
|
|
|
};
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by path', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector(__dirname + '/../lib/connectors/memory');
|
2014-01-10 01:03:37 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by internal name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('memory');
|
2014-01-10 01:03:37 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by module name starting with loopback-connector-', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('loopback-connector-xyz', loader);
|
2014-01-13 19:06:02 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by short module name with full name first', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('xyz', loader);
|
2014-01-13 19:06:02 +00:00
|
|
|
assert(connector.connector);
|
2014-03-13 05:11:55 +00:00
|
|
|
assert.equal(connector.connector.name, 'loopback-connector-xyz');
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by short module name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('abc', loader);
|
2014-03-13 05:11:55 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
assert.equal(connector.connector.name, 'abc');
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by short module name for known connectors', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('oracle', loader);
|
2014-03-13 05:11:55 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
assert.equal(connector.connector.name, 'loopback-connector-oracle');
|
2014-01-13 19:06:02 +00:00
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('resolves connector by full module name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('loopback-xyz', loader);
|
2014-01-13 19:06:02 +00:00
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('fails to resolve connector by module name starting with loopback-connector-', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('loopback-connector-xyz');
|
2014-01-10 01:03:37 +00:00
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-xyz') !== -1);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('fails resolve invalid connector by short module name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('xyz');
|
2014-01-10 01:03:37 +00:00
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-xyz') !== -1);
|
|
|
|
});
|
2016-10-27 20:36:59 +00:00
|
|
|
it('fails to resolve invalid connector by full module name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const connector = DataSource._resolveConnector('loopback-xyz');
|
2014-01-10 01:03:37 +00:00
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-loopback-xyz') !== -1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('Model define with relations configuration', function() {
|
|
|
|
it('sets up hasMany relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String});
|
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {type: 'hasMany', model: 'Post'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(User.relations['posts']);
|
|
|
|
done();
|
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up belongsTo relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String});
|
|
|
|
const Post = ds.define('Post', {userId: Number, content: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {user: {type: 'belongsTo', model: 'User'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(Post.relations['user']);
|
|
|
|
done();
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up referencesMany relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String});
|
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {type: 'referencesMany', model: 'Post'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-08-11 12:43:51 +00:00
|
|
|
assert(User.relations['posts']);
|
|
|
|
done();
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up embedsMany relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String});
|
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {type: 'embedsMany', model: 'Post'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-08-11 12:43:51 +00:00
|
|
|
assert(User.relations['posts']);
|
|
|
|
done();
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2017-04-01 09:13:22 +00:00
|
|
|
it('sets up belongsTo polymorphic relation with `{polymorphic: true}`', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Product = ds.define('Product', {name: String}, {relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
pictures: {type: 'hasMany', model: 'Picture', polymorphic: 'imageable'},
|
2014-08-16 08:23:32 +00:00
|
|
|
}});
|
2018-12-07 14:54:29 +00:00
|
|
|
const Picture = ds.define('Picture', {name: String}, {relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
imageable: {type: 'belongsTo', polymorphic: true},
|
2014-08-16 08:23:32 +00:00
|
|
|
}});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2014-08-16 08:23:32 +00:00
|
|
|
assert(Picture.relations['imageable']);
|
|
|
|
assert.deepEqual(Picture.relations['imageable'].toJSON(), {
|
|
|
|
name: 'imageable',
|
|
|
|
type: 'belongsTo',
|
|
|
|
modelFrom: 'Picture',
|
|
|
|
keyFrom: 'imageableId',
|
|
|
|
modelTo: '<polymorphic>',
|
|
|
|
keyTo: 'id',
|
|
|
|
multiple: false,
|
2015-02-03 10:37:43 +00:00
|
|
|
polymorphic: {
|
2017-04-01 09:13:22 +00:00
|
|
|
selector: 'imageable',
|
|
|
|
foreignKey: 'imageableId',
|
|
|
|
discriminator: 'imageableType',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sets up hasMany polymorphic relation with `{polymorphic: belongsToRelationName}`', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2017-04-01 09:13:22 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Picture = ds.define('Picture', {name: String}, {relations: {
|
2017-04-01 09:13:22 +00:00
|
|
|
imageable: {type: 'belongsTo', polymorphic: true},
|
|
|
|
}});
|
2018-12-07 14:54:29 +00:00
|
|
|
const Product = ds.define('Product', {name: String}, {relations: {
|
2017-04-01 09:13:22 +00:00
|
|
|
pictures: {type: 'hasMany', model: 'Picture', polymorphic: 'imageable'},
|
|
|
|
}});
|
|
|
|
|
|
|
|
assert(Product.relations['pictures']);
|
|
|
|
assert.deepEqual(Product.relations['pictures'].toJSON(), {
|
|
|
|
name: 'pictures',
|
|
|
|
type: 'hasMany',
|
|
|
|
modelFrom: 'Product',
|
|
|
|
keyFrom: 'id',
|
|
|
|
modelTo: 'Picture',
|
|
|
|
keyTo: 'imageableId',
|
|
|
|
multiple: true,
|
|
|
|
polymorphic: {
|
|
|
|
selector: 'imageable',
|
2014-08-16 08:23:32 +00:00
|
|
|
foreignKey: 'imageableId',
|
2016-04-01 11:48:17 +00:00
|
|
|
discriminator: 'imageableType',
|
|
|
|
},
|
2014-08-16 08:23:32 +00:00
|
|
|
});
|
|
|
|
done();
|
|
|
|
});
|
2015-02-03 10:37:43 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('creates a foreign key with the correct type', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-01-21 17:47:32 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String, id: {type: String, id: true}});
|
|
|
|
const Post = ds.define('Post', {content: String}, {relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
user: {type: 'belongsTo', model: 'User'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2014-01-21 17:47:32 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const fk = Post.definition.properties['userId'];
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(fk, 'The foreign key should be added');
|
|
|
|
assert(fk.type === String, 'The foreign key should be the same type as primary key');
|
|
|
|
assert(Post.relations['user'], 'User relation should be set');
|
|
|
|
done();
|
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up related hasMany and belongsTo relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-04-01 13:23:42 +00:00
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
posts: {type: 'hasMany', model: 'Post'},
|
|
|
|
accounts: {type: 'hasMany', model: 'Account'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(!User.relations['posts']);
|
|
|
|
assert(!User.relations['accounts']);
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {user: {type: 'belongsTo', model: 'User'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Account = ds.define('Account', {userId: Number, type: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {user: {type: 'belongsTo', model: 'User'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(Post.relations['user']);
|
2014-06-15 22:53:58 +00:00
|
|
|
assert.deepEqual(Post.relations['user'].toJSON(), {
|
|
|
|
name: 'user',
|
2014-01-24 17:09:53 +00:00
|
|
|
type: 'belongsTo',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelFrom: 'Post',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyFrom: 'userId',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelTo: 'User',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyTo: 'id',
|
2016-04-01 11:48:17 +00:00
|
|
|
multiple: false,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
assert(User.relations['posts']);
|
2014-06-15 22:53:58 +00:00
|
|
|
assert.deepEqual(User.relations['posts'].toJSON(), {
|
|
|
|
name: 'posts',
|
2014-01-24 17:09:53 +00:00
|
|
|
type: 'hasMany',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelFrom: 'User',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyFrom: 'id',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelTo: 'Post',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyTo: 'userId',
|
2016-04-01 11:48:17 +00:00
|
|
|
multiple: true,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
assert(User.relations['accounts']);
|
2014-06-15 22:53:58 +00:00
|
|
|
assert.deepEqual(User.relations['accounts'].toJSON(), {
|
|
|
|
name: 'accounts',
|
2014-01-24 17:09:53 +00:00
|
|
|
type: 'hasMany',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelFrom: 'User',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyFrom: 'id',
|
2014-06-15 22:53:58 +00:00
|
|
|
modelTo: 'Account',
|
2014-01-24 17:09:53 +00:00
|
|
|
keyTo: 'userId',
|
2016-04-01 11:48:17 +00:00
|
|
|
multiple: true,
|
2013-11-05 06:53:02 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
done();
|
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if a relation is missing type', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {model: 'Post'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
} catch (e) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if a relation type is invalid', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = ds.define('Post', {userId: Number, content: String});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
try {
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = ds.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {type: 'hasXYZ', model: 'Post'}},
|
2016-04-01 13:23:42 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
} catch (e) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up hasMany through relations', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Physician = ds.createModel('Physician', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
patients: {model: 'Patient', type: 'hasMany', through: 'Appointment'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Patient = ds.createModel('Patient', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
physicians: {model: 'Physician', type: 'hasMany', through: 'Appointment'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(!Physician.relations['patients']); // Appointment hasn't been resolved yet
|
|
|
|
assert(!Patient.relations['physicians']); // Appointment hasn't been resolved yet
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Appointment = ds.createModel('Appointment', {
|
2014-01-24 17:09:53 +00:00
|
|
|
physicianId: Number,
|
|
|
|
patientId: Number,
|
2016-04-01 11:48:17 +00:00
|
|
|
appointmentDate: Date,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
patient: {type: 'belongsTo', model: 'Patient'},
|
|
|
|
physician: {type: 'belongsTo', model: 'Physician'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(Physician.relations['patients']);
|
|
|
|
assert(Patient.relations['physicians']);
|
|
|
|
done();
|
|
|
|
});
|
2013-11-07 21:28:18 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up hasMany through relations with options', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Physician = ds.createModel('Physician', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
patients: {model: 'Patient', type: 'hasMany', foreignKey: 'leftId', through: 'Appointment'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2014-09-01 15:59:52 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Patient = ds.createModel('Patient', {
|
2016-04-01 11:48:17 +00:00
|
|
|
name: String,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
physicians: {model: 'Physician', type: 'hasMany', foreignKey: 'rightId', through: 'Appointment'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2014-09-01 15:59:52 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Appointment = ds.createModel('Appointment', {
|
2014-09-01 15:59:52 +00:00
|
|
|
physicianId: Number,
|
|
|
|
patientId: Number,
|
2016-04-01 11:48:17 +00:00
|
|
|
appointmentDate: Date,
|
2016-04-01 13:23:42 +00:00
|
|
|
}, {
|
|
|
|
relations: {
|
2016-08-19 17:46:59 +00:00
|
|
|
patient: {type: 'belongsTo', model: 'Patient'},
|
|
|
|
physician: {type: 'belongsTo', model: 'Physician'},
|
2016-04-01 13:23:42 +00:00
|
|
|
},
|
|
|
|
});
|
2014-09-01 15:59:52 +00:00
|
|
|
|
|
|
|
assert(Physician.relations['patients'].keyTo === 'leftId');
|
|
|
|
assert(Patient.relations['physicians'].keyTo === 'rightId');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('sets up relations after attach', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const modelBuilder = new ModelBuilder();
|
2013-11-07 21:28:18 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Post = modelBuilder.define('Post', {userId: Number, content: String});
|
|
|
|
const User = modelBuilder.define('User', {name: String}, {
|
2016-08-19 17:46:59 +00:00
|
|
|
relations: {posts: {type: 'hasMany', model: 'Post'},
|
2016-12-05 14:14:09 +00:00
|
|
|
}});
|
2013-11-07 21:28:18 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(!User.relations['posts']);
|
|
|
|
Post.attachTo(ds);
|
|
|
|
User.attachTo(ds);
|
|
|
|
assert(User.relations['posts']);
|
|
|
|
done();
|
|
|
|
});
|
2013-11-05 06:53:02 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('Model define with scopes configuration', function() {
|
|
|
|
it('creates scopes', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const User = ds.define('User', {name: String, vip: Boolean, age: Number},
|
2016-08-19 17:46:59 +00:00
|
|
|
{scopes: {vips: {where: {vip: true}}, top5: {limit: 5, order: 'age'}}});
|
2014-03-04 01:16:37 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const users = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
2016-08-19 17:46:59 +00:00
|
|
|
users.push({name: 'User' + i, vip: i % 3 === 0, age: 20 + i * 2});
|
2014-03-04 01:16:37 +00:00
|
|
|
}
|
2016-04-01 11:48:17 +00:00
|
|
|
async.each(users, function(user, callback) {
|
2014-03-04 01:16:37 +00:00
|
|
|
User.create(user, callback);
|
2016-04-01 11:48:17 +00:00
|
|
|
}, function(err) {
|
|
|
|
User.vips(function(err, vips) {
|
|
|
|
if (err) {
|
2014-03-04 01:16:37 +00:00
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
assert.equal(vips.length, 4);
|
2016-04-01 11:48:17 +00:00
|
|
|
User.top5(function(err, top5) {
|
2014-03-04 01:16:37 +00:00
|
|
|
assert.equal(top5.length, 5);
|
|
|
|
done(err);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('DataAccessObject', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
let ds, model, where, error, filter;
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
before(function() {
|
2013-12-04 18:00:33 +00:00
|
|
|
ds = new DataSource('memory');
|
|
|
|
model = ds.createModel('M1', {
|
2016-08-19 17:46:59 +00:00
|
|
|
id: {type: String, id: true},
|
2013-12-04 18:00:33 +00:00
|
|
|
age: Number,
|
2017-03-23 00:29:01 +00:00
|
|
|
string: 'string',
|
2013-12-04 18:00:33 +00:00
|
|
|
vip: Boolean,
|
|
|
|
date: Date,
|
2013-12-14 17:49:11 +00:00
|
|
|
location: 'GeoPoint',
|
2016-04-01 11:48:17 +00:00
|
|
|
scores: [Number],
|
2017-03-20 14:05:11 +00:00
|
|
|
array: 'array',
|
|
|
|
object: 'object',
|
2013-12-04 05:27:46 +00:00
|
|
|
});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
beforeEach(function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
error = null;
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause for string types', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({id: 1});
|
|
|
|
assert.deepEqual(where, {id: '1'});
|
|
|
|
where = model._coerce({id: '1'});
|
|
|
|
assert.deepEqual(where, {id: '1'});
|
2015-07-01 21:58:14 +00:00
|
|
|
|
|
|
|
// Mockup MongoDB ObjectID
|
|
|
|
function ObjectID(id) {
|
|
|
|
this.id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjectID.prototype.toString = function() {
|
|
|
|
return this.id;
|
|
|
|
};
|
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({id: new ObjectID('1')});
|
|
|
|
assert.deepEqual(where, {id: '1'});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause for number types', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: '10'});
|
|
|
|
assert.deepEqual(where, {age: 10});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: 10});
|
|
|
|
assert.deepEqual(where, {age: 10});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: {gt: 10}});
|
|
|
|
assert.deepEqual(where, {age: {gt: 10}});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: {gt: '10'}});
|
|
|
|
assert.deepEqual(where, {age: {gt: 10}});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: {between: ['10', '20']}});
|
|
|
|
assert.deepEqual(where, {age: {between: [10, 20]}});
|
2013-12-04 05:27:46 +00:00
|
|
|
});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause for array types', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({scores: ['10', '20']});
|
|
|
|
assert.deepEqual(where, {scores: [10, 20]});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause for date types', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const d = new Date();
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({date: d});
|
|
|
|
assert.deepEqual(where, {date: d});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({date: d.toISOString()});
|
|
|
|
assert.deepEqual(where, {date: d});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause for boolean types', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: 'true'});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: true});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: 'false'});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: false});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: '1'});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: 0});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({vip: ''});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
2013-12-14 17:49:11 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause with and operators', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({and: [{age: '10'}, {vip: 'true'}]});
|
|
|
|
assert.deepEqual(where, {and: [{age: 10}, {vip: true}]});
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('coerces where clause with or operators', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({or: [{age: '10'}, {vip: 'true'}]});
|
|
|
|
assert.deepEqual(where, {or: [{age: 10}, {vip: true}]});
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
|
|
|
|
2016-10-18 20:02:14 +00:00
|
|
|
it('continues to coerce properties after a logical operator', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const clause = {and: [{age: '10'}], vip: 'true'};
|
2016-10-18 20:02:14 +00:00
|
|
|
|
|
|
|
// Key order is predictable but not guaranteed. We prefer false negatives (failure) to false positives.
|
|
|
|
assert(Object.keys(clause)[0] === 'and', 'Unexpected key order.');
|
|
|
|
|
|
|
|
where = model._coerce(clause);
|
|
|
|
assert.deepEqual(where, {and: [{age: 10}], vip: true});
|
|
|
|
});
|
|
|
|
|
2016-10-18 20:39:30 +00:00
|
|
|
const COERCIONS = [
|
|
|
|
{
|
|
|
|
in: {scores: {0: '10', 1: '20'}},
|
|
|
|
out: {scores: [10, 20]},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: {and: {0: {age: '10'}, 1: {vip: 'true'}}},
|
|
|
|
out: {and: [{age: 10}, {vip: true}]},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: {or: {0: {age: '10'}, 1: {vip: 'true'}}},
|
|
|
|
out: {or: [{age: 10}, {vip: true}]},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: {id: {inq: {0: 'aaa', 1: 'bbb'}}},
|
|
|
|
out: {id: {inq: ['aaa', 'bbb']}},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: {id: {nin: {0: 'aaa', 1: 'bbb'}}},
|
|
|
|
out: {id: {nin: ['aaa', 'bbb']}},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: {scores: {between: {0: '0', 1: '42'}}},
|
|
|
|
out: {scores: {between: [0, 42]}},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
COERCIONS.forEach(coercion => {
|
2018-12-07 14:54:29 +00:00
|
|
|
const inStr = JSON.stringify(coercion.in);
|
2016-10-18 20:39:30 +00:00
|
|
|
it('coerces where clause with array-like objects ' + inStr, () => {
|
|
|
|
assert.deepEqual(model._coerce(coercion.in), coercion.out);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const INVALID_CLAUSES = [
|
|
|
|
{scores: {inq: {0: '10', 1: '20', 4: '30'}}},
|
|
|
|
{scores: {inq: {0: '10', 1: '20', bogus: 'true'}}},
|
|
|
|
{scores: {between: {0: '10', 1: '20', 2: '30'}}},
|
|
|
|
];
|
|
|
|
|
|
|
|
INVALID_CLAUSES.forEach((where) => {
|
2018-12-07 14:54:29 +00:00
|
|
|
const whereStr = JSON.stringify(where);
|
2016-10-18 20:39:30 +00:00
|
|
|
it('throws an error on malformed array-like object ' + whereStr, () => {
|
|
|
|
assert.throws(() => model._coerce(where), /property has invalid clause/);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the where property is not an object', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The where clause has to be an object
|
2014-06-04 21:23:53 +00:00
|
|
|
model._coerce('abc');
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the where property is an array', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The where clause cannot be an array
|
2014-06-04 21:23:53 +00:00
|
|
|
model._coerce([
|
2016-08-19 17:46:59 +00:00
|
|
|
{vip: true},
|
2014-06-02 06:31:51 +00:00
|
|
|
]);
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the and operator is not configured with an array', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The and operator only takes an array of objects
|
2016-08-19 17:46:59 +00:00
|
|
|
model._coerce({and: {x: 1}});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the or operator does not take an array', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The or operator only takes an array of objects
|
2016-08-19 17:46:59 +00:00
|
|
|
model._coerce({or: {x: 1}});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the or operator not configured with an array of objects', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The or operator only takes an array of objects
|
2016-08-19 17:46:59 +00:00
|
|
|
model._coerce({or: ['x']});
|
2016-04-01 11:48:17 +00:00
|
|
|
} catch (err) {
|
2014-06-02 06:31:51 +00:00
|
|
|
error = err;
|
|
|
|
}
|
2016-10-18 20:02:14 +00:00
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('throws an error when malformed logical operators follow valid logical clauses', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const invalid = {and: [{x: 1}], or: 'bogus'};
|
2016-10-18 20:02:14 +00:00
|
|
|
|
|
|
|
// Key order is predictable but not guaranteed. We prefer false negatives (failure) to false positives.
|
|
|
|
assert(Object.keys(invalid)[0] !== 'or', 'Unexpected key order.');
|
|
|
|
|
|
|
|
try {
|
|
|
|
model._coerce(invalid);
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
2014-06-02 06:31:51 +00:00
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the filter property is not an object', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
let filter = null;
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The filter clause has to be an object
|
|
|
|
filter = model._normalize('abc');
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the filter.limit property is not a number', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The limit param must be a valid number
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({limit: 'x'});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2017-03-20 14:05:11 +00:00
|
|
|
it('throws an error if the filter.limit property is negative', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The limit param must be a valid number
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({limit: -1});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the filter.limit property is not an integer', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The limit param must be a valid number
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({limit: 5.8});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if filter.offset property is not a number', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The limit param must be a valid number
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({offset: 'x'});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('throws an error if the filter.skip property is not a number', function() {
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
// The limit param must be a valid number
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({skip: '_'});
|
2014-06-02 06:31:51 +00:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
}
|
|
|
|
assert(error, 'An error should have been thrown');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('normalizes limit/offset/skip', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({limit: '10', skip: 5});
|
|
|
|
assert.deepEqual(filter, {limit: 10, offset: 5, skip: 5});
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses a default value for limit', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({skip: 5});
|
|
|
|
assert.deepEqual(filter, {limit: 100, offset: 5, skip: 5});
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('applies settings for handling undefined', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({filter: {x: undefined}});
|
|
|
|
assert.deepEqual(filter, {filter: {}});
|
2015-09-29 21:40:47 +00:00
|
|
|
|
|
|
|
ds.settings.normalizeUndefinedInQuery = 'ignore';
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({filter: {x: undefined}});
|
|
|
|
assert.deepEqual(filter, {filter: {}}, 'Should ignore undefined');
|
2015-09-29 21:40:47 +00:00
|
|
|
|
|
|
|
ds.settings.normalizeUndefinedInQuery = 'nullify';
|
2016-08-19 17:46:59 +00:00
|
|
|
filter = model._normalize({filter: {x: undefined}});
|
|
|
|
assert.deepEqual(filter, {filter: {x: null}}, 'Should nullify undefined');
|
2015-09-29 21:40:47 +00:00
|
|
|
|
|
|
|
ds.settings.normalizeUndefinedInQuery = 'throw';
|
2016-08-19 17:46:59 +00:00
|
|
|
(function() { model._normalize({filter: {x: undefined}}); }).should.throw(/`undefined` in query/);
|
2015-09-29 21:40:47 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('does not coerce GeoPoint', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({location: {near: {lng: 10, lat: 20}, maxDistance: 20}});
|
|
|
|
assert.deepEqual(where, {location: {near: {lng: 10, lat: 20}, maxDistance: 20}});
|
2013-12-14 17:49:11 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('does not coerce null values', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({date: null});
|
|
|
|
assert.deepEqual(where, {date: null});
|
2013-12-16 16:36:15 +00:00
|
|
|
});
|
2013-12-14 17:54:28 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('does not coerce undefined values', function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({date: undefined});
|
|
|
|
assert.deepEqual(where, {date: undefined});
|
2013-12-14 17:54:28 +00:00
|
|
|
});
|
|
|
|
|
2017-03-20 14:05:11 +00:00
|
|
|
it('does not coerce empty objects to arrays', function() {
|
|
|
|
where = model._coerce({object: {}});
|
|
|
|
where.object.should.not.be.an.Array();
|
|
|
|
where.object.should.be.an.Object();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not coerce an empty array', function() {
|
|
|
|
where = model._coerce({array: []});
|
|
|
|
where.array.should.be.an.Array();
|
|
|
|
where.array.should.have.length(0);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('does not coerce to a number for a simple value that produces NaN',
|
2016-04-01 11:48:17 +00:00
|
|
|
function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: 'xyz'});
|
|
|
|
assert.deepEqual(where, {age: 'xyz'});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-12-16 06:51:47 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('does not coerce to a number for a simple value in an array that produces NaN',
|
2016-04-01 11:48:17 +00:00
|
|
|
function() {
|
2016-08-19 17:46:59 +00:00
|
|
|
where = model._coerce({age: {inq: ['xyz', '12']}});
|
|
|
|
assert.deepEqual(where, {age: {inq: ['xyz', 12]}});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-12-16 06:51:47 +00:00
|
|
|
|
2017-03-23 00:29:01 +00:00
|
|
|
it('does not coerce to a string for a regexp value in an array ',
|
|
|
|
function() {
|
|
|
|
where = model._coerce({string: {inq: [/xyz/i, new RegExp(/xyz/i)]}});
|
|
|
|
assert.deepEqual(where, {string: {inq: [/xyz/i, /xyz/i]}});
|
|
|
|
});
|
|
|
|
|
2015-09-29 21:40:47 +00:00
|
|
|
// settings
|
2016-10-27 20:36:59 +00:00
|
|
|
it('gets settings in priority',
|
2016-04-01 11:48:17 +00:00
|
|
|
function() {
|
2015-09-29 21:40:47 +00:00
|
|
|
ds.settings.test = 'test';
|
|
|
|
assert.equal(model._getSetting('test'), ds.settings.test, 'Should get datasource setting');
|
|
|
|
ds.settings.test = undefined;
|
|
|
|
|
|
|
|
model.settings.test = 'test';
|
|
|
|
assert.equal(model._getSetting('test'), model.settings.test, 'Should get model settings');
|
|
|
|
|
|
|
|
ds.settings.test = 'willNotGet';
|
|
|
|
assert.notEqual(model._getSetting('test'), ds.settings.test, 'Should not get datasource setting');
|
|
|
|
});
|
2013-12-04 05:27:46 +00:00
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
describe('ModelBuilder processing json files', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const path = require('path'),
|
2014-09-12 21:25:35 +00:00
|
|
|
fs = require('fs');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load LDL schemas from a json doc
|
|
|
|
* @param schemaFile The dataSource json file
|
|
|
|
* @returns A map of schemas keyed by name
|
|
|
|
*/
|
|
|
|
function loadSchemasSync(schemaFile, dataSource) {
|
2018-12-07 14:54:29 +00:00
|
|
|
let modelBuilder, createModel;
|
2014-09-12 21:25:35 +00:00
|
|
|
// Set up the data source
|
|
|
|
if (!dataSource) {
|
|
|
|
modelBuilder = new ModelBuilder();
|
|
|
|
} else {
|
|
|
|
modelBuilder = dataSource.modelBuilder;
|
|
|
|
createModel = dataSource.createModel.bind(dataSource);
|
|
|
|
}
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-09-12 21:25:35 +00:00
|
|
|
// Read the dataSource JSON file
|
2018-12-07 14:54:29 +00:00
|
|
|
const schemas = JSON.parse(fs.readFileSync(schemaFile));
|
2014-09-12 21:25:35 +00:00
|
|
|
return modelBuilder.buildModels(schemas, createModel);
|
|
|
|
}
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('defines models', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
let models = loadSchemasSync(path.join(__dirname, 'test1-schemas.json'));
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
models.should.have.property('AnonymousModel_0');
|
|
|
|
models.AnonymousModel_0.should.have.property('modelName', 'AnonymousModel_0');
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const m1 = new models.AnonymousModel_0({title: 'Test'});
|
2014-01-24 17:09:53 +00:00
|
|
|
m1.should.have.property('title', 'Test');
|
|
|
|
m1.should.have.property('author', 'Raymond');
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
models = loadSchemasSync(path.join(__dirname, 'test2-schemas.json'));
|
|
|
|
models.should.have.property('Address');
|
|
|
|
models.should.have.property('Account');
|
|
|
|
models.should.have.property('Customer');
|
2018-12-07 14:54:29 +00:00
|
|
|
for (const s in models) {
|
|
|
|
const m = models[s];
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(new m());
|
|
|
|
}
|
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('attaches models to a specified dataSource', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-09-12 21:25:35 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const models = loadSchemasSync(path.join(__dirname, 'test2-schemas.json'), ds);
|
2014-09-12 21:25:35 +00:00
|
|
|
models.should.have.property('Address');
|
|
|
|
models.should.have.property('Account');
|
|
|
|
models.should.have.property('Customer');
|
|
|
|
assert.equal(models.Address.dataSource, ds);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('allows customization of default model base class', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2014-07-08 21:04:20 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {
|
2014-07-08 21:04:20 +00:00
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
2016-04-01 11:48:17 +00:00
|
|
|
age: Number,
|
2014-07-08 21:04:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
modelBuilder.defaultModelBaseClass = User;
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = modelBuilder.define('Customer', {customerId: {type: String, id: true}});
|
2014-07-08 21:04:20 +00:00
|
|
|
assert(Customer.prototype instanceof User);
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('accepts a model base class', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const modelBuilder = new ModelBuilder();
|
2014-07-08 21:04:20 +00:00
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const User = modelBuilder.define('User', {
|
2014-07-08 21:04:20 +00:00
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
2016-04-01 11:48:17 +00:00
|
|
|
age: Number,
|
2014-07-08 21:04:20 +00:00
|
|
|
});
|
|
|
|
|
2018-12-07 14:54:29 +00:00
|
|
|
const Customer = modelBuilder.define('Customer',
|
2016-08-19 17:46:59 +00:00
|
|
|
{customerId: {type: String, id: true}}, {}, User);
|
2014-07-08 21:04:20 +00:00
|
|
|
assert(Customer.prototype instanceof User);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('DataSource constructor', function() {
|
2016-10-27 20:36:59 +00:00
|
|
|
it('takes url as the settings', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory://localhost/mydb?x=1');
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('takes connector name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('takes settings object', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource({connector: 'memory'});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('takes settings object and name', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('x', {connector: 'memory'});
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
|
|
|
});
|
2013-12-04 05:14:12 +00:00
|
|
|
|
2016-04-01 11:48:17 +00:00
|
|
|
describe('ModelBuilder options.models', function() {
|
2016-10-27 20:36:59 +00:00
|
|
|
it('injects model classes from models', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
|
|
|
const M1 = builder.define('M1');
|
|
|
|
const M2 = builder.define('M2', {}, {models: {
|
2016-04-01 11:48:17 +00:00
|
|
|
'M1': M1,
|
2013-12-19 00:13:41 +00:00
|
|
|
}});
|
|
|
|
|
|
|
|
assert.equal(M2.M1, M1, 'M1 should be injected to M2');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('injects model classes by name in the models', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
|
|
|
const M1 = builder.define('M1');
|
|
|
|
const M2 = builder.define('M2', {}, {models: {
|
2016-04-01 11:48:17 +00:00
|
|
|
'M1': 'M1',
|
2013-12-19 00:13:41 +00:00
|
|
|
}});
|
|
|
|
|
|
|
|
assert.equal(M2.M1, M1, 'M1 should be injected to M2');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('injects model classes by name in the models before the class is defined',
|
2016-04-01 11:48:17 +00:00
|
|
|
function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
|
|
|
const M2 = builder.define('M2', {}, {models: {
|
2016-04-01 11:48:17 +00:00
|
|
|
'M1': 'M1',
|
2013-12-19 00:13:41 +00:00
|
|
|
}});
|
|
|
|
assert(M2.M1, 'M1 should be injected to M2');
|
|
|
|
assert(M2.M1.settings.unresolved, 'M1 is still a proxy');
|
2018-12-07 14:54:29 +00:00
|
|
|
const M1 = builder.define('M1');
|
2013-12-19 00:13:41 +00:00
|
|
|
assert.equal(M2.M1, M1, 'M1 should be injected to M2');
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-12-19 00:13:41 +00:00
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses non-strict mode for embedded models by default', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
|
|
|
const M1 = builder.define('testEmbedded', {
|
2016-05-27 22:21:53 +00:00
|
|
|
name: 'string',
|
|
|
|
address: {
|
|
|
|
street: 'string',
|
|
|
|
},
|
|
|
|
});
|
2018-12-07 14:54:29 +00:00
|
|
|
const m1 = new M1({
|
2016-05-27 22:21:53 +00:00
|
|
|
name: 'Jim',
|
|
|
|
address: {
|
|
|
|
street: 'washington st',
|
|
|
|
number: 5512,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
assert.equal(m1.address.number, 5512, 'm1 should contain number property in address');
|
|
|
|
});
|
|
|
|
|
2016-10-27 20:36:59 +00:00
|
|
|
it('uses the strictEmbeddedModels setting (true) when applied on modelBuilder', function() {
|
2018-12-07 14:54:29 +00:00
|
|
|
const builder = new ModelBuilder();
|
2016-05-27 22:21:53 +00:00
|
|
|
builder.settings.strictEmbeddedModels = true;
|
2018-12-07 14:54:29 +00:00
|
|
|
const M1 = builder.define('testEmbedded', {
|
2016-05-27 22:21:53 +00:00
|
|
|
name: 'string',
|
|
|
|
address: {
|
|
|
|
street: 'string',
|
|
|
|
},
|
|
|
|
});
|
2018-12-07 14:54:29 +00:00
|
|
|
const m1 = new M1({
|
2016-05-27 22:21:53 +00:00
|
|
|
name: 'Jim',
|
|
|
|
address: {
|
|
|
|
street: 'washington st',
|
|
|
|
number: 5512,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
assert.equal(m1.address.number, undefined, 'm1 should not contain number property in address');
|
|
|
|
assert.equal(m1.address.isValid(), false, 'm1 address should not validate with extra property');
|
2018-12-07 14:54:29 +00:00
|
|
|
const codes = m1.address.errors && m1.address.errors.codes || {};
|
2016-05-27 22:21:53 +00:00
|
|
|
assert.deepEqual(codes.number, ['unknown-property']);
|
|
|
|
});
|
2013-12-19 01:14:54 +00:00
|
|
|
});
|
2017-08-22 20:09:45 +00:00
|
|
|
|
|
|
|
describe('updateOnly', function() {
|
|
|
|
it('sets forceId to true when model id is generated', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Post = ds.define('Post', {
|
2017-08-22 20:09:45 +00:00
|
|
|
title: {type: String, length: 255},
|
|
|
|
date: {type: Date, default: function() {
|
|
|
|
return new Date();
|
|
|
|
}},
|
|
|
|
});
|
|
|
|
// check if forceId is added as true in ModelClass's settings[] explicitly,
|
|
|
|
// if id a generated (default) and forceId in from the model is
|
|
|
|
// true(unspecified is 'true' which is the default).
|
|
|
|
Post.settings.should.have.property('forceId').eql('auto');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('flags id as updateOnly when forceId is undefined', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Post = ds.define('Post', {
|
2017-08-22 20:09:45 +00:00
|
|
|
title: {type: String, length: 255},
|
|
|
|
date: {type: Date, default: function() {
|
|
|
|
return new Date();
|
|
|
|
}},
|
|
|
|
});
|
|
|
|
// check if method getUpdateOnlyProperties exist in ModelClass and check if
|
|
|
|
// the Post has 'id' in updateOnlyProperties list
|
|
|
|
Post.should.have.property('getUpdateOnlyProperties');
|
|
|
|
Post.getUpdateOnlyProperties().should.eql(['id']);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not flag id as updateOnly when forceId is false', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Person = ds.define('Person', {
|
2017-08-22 20:09:45 +00:00
|
|
|
name: String,
|
|
|
|
gender: String,
|
|
|
|
}, {forceId: false});
|
|
|
|
// id should not be there in updateOnly properties list if forceId is set
|
|
|
|
// to false
|
|
|
|
Person.should.have.property('getUpdateOnlyProperties');
|
|
|
|
Person.getUpdateOnlyProperties().should.eql([]);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('flags id as updateOnly when forceId is true', function(done) {
|
2018-12-07 14:54:29 +00:00
|
|
|
const ds = new DataSource('memory');
|
|
|
|
const Person = ds.define('Person', {
|
2017-08-22 20:09:45 +00:00
|
|
|
name: String,
|
|
|
|
gender: String,
|
|
|
|
}, {forceId: true});
|
|
|
|
// id should be there in updateOnly properties list if forceId is set
|
|
|
|
// to true
|
|
|
|
Person.should.have.property('getUpdateOnlyProperties');
|
|
|
|
Person.getUpdateOnlyProperties().should.eql(['id']);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|