2013-05-28 20:50:59 +00:00
|
|
|
// This test written in mocha+should.js
|
|
|
|
var should = require('./init.js');
|
2013-07-12 19:39:38 +00:00
|
|
|
var assert = require('assert');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
|
|
|
var jdb = require('../');
|
|
|
|
var ModelBuilder = jdb.ModelBuilder;
|
|
|
|
var DataSource = jdb.DataSource;
|
|
|
|
|
|
|
|
describe('ModelBuilder define model', function () {
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define plain models', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {
|
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
|
|
|
age: Number
|
2013-07-25 05:54:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// define any custom method
|
|
|
|
User.prototype.getNameAndAge = function () {
|
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
modelBuilder.models.should.be.a('object').and.have.property('User', User);
|
|
|
|
modelBuilder.definitions.should.be.a('object').and.have.property('User');
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user = new User({name: 'Joe', age: 20});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
done(null, User);
|
|
|
|
});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should not take unknown properties in strict mode', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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');
|
|
|
|
user.should.be.a('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');
|
|
|
|
user.should.not.have.property('bio');
|
2014-01-24 17:09:53 +00:00
|
|
|
done(null, User);
|
|
|
|
});
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-30 19:51:34 +00:00
|
|
|
it('should ignore non-predefined properties in strict mode', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
|
|
|
|
|
|
|
var User = modelBuilder.define('User', {name: String, bio: String}, {strict: true});
|
|
|
|
|
|
|
|
var user = new User({name: 'Joe'});
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should throw when unknown properties are used if strict=throw', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-07-25 05:54:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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 {
|
|
|
|
var user = new User({name: 'Joe', age: 20});
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define open models', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {}, {strict: false});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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');
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
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-30 19:51:34 +00:00
|
|
|
it('should take non-predefined properties in non-strict mode', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
|
|
|
|
|
|
|
var User = modelBuilder.define('User', {name: String, bio: String}, {strict: false});
|
|
|
|
|
|
|
|
var user = new User({name: 'Joe'});
|
|
|
|
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');
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should use false as the default value for strict', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {});
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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');
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define nesting models', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-07-11 21:24:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// simplier way to describe model
|
|
|
|
var User = modelBuilder.define('User', {
|
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
|
|
|
age: Number,
|
|
|
|
address: {
|
|
|
|
street: String,
|
|
|
|
city: String,
|
|
|
|
state: String,
|
|
|
|
zipCode: String,
|
|
|
|
country: String
|
|
|
|
},
|
|
|
|
emails: [
|
|
|
|
{
|
|
|
|
label: String,
|
|
|
|
email: String
|
|
|
|
}
|
|
|
|
],
|
|
|
|
friends: [String]
|
2013-07-11 21:24:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// define any custom method
|
|
|
|
User.prototype.getNameAndAge = function () {
|
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
modelBuilder.models.should.be.a('object').and.have.property('User', User);
|
|
|
|
modelBuilder.definitions.should.be.a('object').and.have.property('User');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user = new User({
|
|
|
|
name: 'Joe', age: 20,
|
|
|
|
address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
|
|
|
emails: [
|
|
|
|
{label: 'work', email: 'xyz@sample.com'}
|
|
|
|
],
|
|
|
|
friends: ['Mary', 'John']
|
|
|
|
});
|
|
|
|
|
|
|
|
User.modelName.should.equal('User');
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('name', 'Joe');
|
|
|
|
user.should.have.property('age', 20);
|
|
|
|
user.should.not.have.property('bio');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to reference models by name before they are defined', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {name: String, address: 'Address'});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user;
|
|
|
|
try {
|
|
|
|
user = new User({name: 'Joe', address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'}});
|
|
|
|
assert(false, 'An exception should have been thrown');
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
var Address = modelBuilder.define('Address', {
|
|
|
|
street: String,
|
|
|
|
city: String,
|
|
|
|
state: String,
|
|
|
|
zipCode: String,
|
|
|
|
country: String
|
2013-11-05 06:53:02 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +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);
|
|
|
|
user.should.be.a('object');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
|
|
|
describe('DataSource define model', function () {
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define plain models', function () {
|
|
|
|
var ds = new DataSource('memory');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
|
|
|
// define models
|
2014-01-24 17:09:53 +00:00
|
|
|
var Post = ds.define('Post', {
|
|
|
|
title: { type: String, length: 255 },
|
|
|
|
content: { type: ModelBuilder.Text },
|
|
|
|
date: { type: Date, default: function () {
|
|
|
|
return new Date();
|
|
|
|
} },
|
|
|
|
timestamp: { type: Number, default: Date.now },
|
|
|
|
published: { type: Boolean, default: false, index: true }
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2013-08-27 17:14:49 +00:00
|
|
|
// simpler way to describe model
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {
|
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
|
|
|
age: Number
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Group = ds.define('Group', {group: String});
|
|
|
|
User.mixin(Group);
|
2013-05-28 20:50:59 +00:00
|
|
|
|
|
|
|
// define any custom method
|
2014-01-24 17:09:53 +00:00
|
|
|
User.prototype.getNameAndAge = function () {
|
|
|
|
return this.name + ', ' + this.age;
|
|
|
|
};
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user = new User({name: 'Joe', group: 'G1'});
|
|
|
|
assert.equal(user.name, 'Joe');
|
|
|
|
assert.equal(user.group, 'G1');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// setup relationships
|
|
|
|
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var user2 = new User({name: 'Smith'});
|
|
|
|
user2.save(function (err) {
|
|
|
|
var post = user2.posts.build({title: 'Hello world'});
|
|
|
|
post.save(function (err, data) {
|
|
|
|
// console.log(err ? err : data);
|
|
|
|
});
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Post.findOne({where: {published: false}, order: 'date DESC'}, function (err, data) {
|
|
|
|
// console.log(data);
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.create({name: 'Jeff'}, function (err, data) {
|
|
|
|
if (err) {
|
|
|
|
console.log(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var post = data.posts.build({title: 'My Post'});
|
|
|
|
});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.create({name: 'Ray'}, function (err, data) {
|
|
|
|
// console.log(data);
|
|
|
|
});
|
|
|
|
|
|
|
|
var Article = ds.define('Article', {title: String});
|
|
|
|
var Tag = ds.define('Tag', {name: String});
|
|
|
|
Article.hasAndBelongsToMany('tags');
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Article.create(function (e, article) {
|
|
|
|
article.tags.create({name: 'popular'}, function (err, data) {
|
|
|
|
Article.findOne(function (e, article) {
|
|
|
|
article.tags(function (e, tags) {
|
|
|
|
// 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
|
|
|
|
|
|
|
// should be able to attach a data source to an existing model
|
2014-01-24 17:09:53 +00:00
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Color = modelBuilder.define('Color', {
|
|
|
|
name: String
|
|
|
|
});
|
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
|
|
|
|
|
|
|
// 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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Color.create({name: 'red'});
|
|
|
|
Color.create({name: 'green'});
|
|
|
|
Color.create({name: 'blue'});
|
2013-05-28 20:50:59 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Color.all(function (err, colors) {
|
|
|
|
colors.should.have.lengthOf(3);
|
2013-05-28 20:50:59 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should not take unknown properties in strict mode', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function (err, user) {
|
|
|
|
|
|
|
|
User.modelName.should.equal('User');
|
|
|
|
user.should.be.a('object');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should throw when unknown properties are used if strict=throw', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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 {
|
|
|
|
var user = new User({name: 'Joe', age: 20});
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define open models', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {}, {strict: false});
|
|
|
|
User.modelName.should.equal('User');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function (err, user) {
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.findById(user.id, function (err, user) {
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should use false as the default value for strict', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.create({name: 'Joe', age: 20}, function (err, user) {
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
User.modelName.should.equal('User');
|
|
|
|
user.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should use true as the default value for strict for relational DBs', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
ds.connector.relational = true; // HACK
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {name: String, bio: String}, {strict: true});
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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');
|
|
|
|
user.should.be.a('object');
|
|
|
|
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
|
|
|
it('should throw when unknown properties are used if strict=false for relational DBs', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
ds.connector.relational = true; // HACK
|
2013-08-26 20:38:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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 {
|
|
|
|
var user = new User({name: 'Joe', age: 20});
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should change the property value for save if strict=false', function (done) {
|
|
|
|
var ds = new DataSource('memory');// define models
|
|
|
|
var Post = ds.define('Post');
|
2013-09-18 23:34:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
Post.create({price: 900}, function (err, post) {
|
|
|
|
assert.equal(post.price, 900);
|
|
|
|
post.price = 1000;
|
|
|
|
post.save(function (err, result) {
|
|
|
|
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
|
|
|
|
2014-02-11 06:46:25 +00:00
|
|
|
it('supports instance level strict mode', function () {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
|
|
|
|
var User = ds.define('User', {name: String, bio: String}, {strict: true});
|
|
|
|
|
|
|
|
var user = new User({name: 'Joe', age: 20}, {strict: false});
|
|
|
|
|
|
|
|
user.should.have.property('__strict', false);
|
|
|
|
user.should.be.a('object');
|
|
|
|
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);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2014-02-25 02:38:45 +00:00
|
|
|
it('should update the instance with unknown properties', function (done) {
|
|
|
|
var ds = new DataSource('memory');// define models
|
|
|
|
Post = ds.define('Post', {
|
|
|
|
title: { type: String, length: 255, index: true },
|
|
|
|
content: { type: String }
|
|
|
|
});
|
|
|
|
|
|
|
|
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
|
|
|
post.updateAttributes({title: 'b', xyz: 'xyz'}, function (err, p) {
|
|
|
|
should.not.exist(err);
|
|
|
|
p.id.should.be.equal(post.id);
|
|
|
|
p.content.should.be.equal(post.content);
|
|
|
|
p.xyz.should.be.equal('xyz');
|
|
|
|
|
|
|
|
Post.findById(post.id, function (err, p) {
|
|
|
|
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();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-01-24 22:51:01 +00:00
|
|
|
it('injects id by default', function (done) {
|
|
|
|
var ds = new ModelBuilder();
|
|
|
|
|
|
|
|
var User = ds.define('User', {});
|
|
|
|
assert.deepEqual(User.definition.properties.id,
|
|
|
|
{type: Number, id: 1, generated: true});
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('disables idInjection if the value is false', function (done) {
|
|
|
|
var ds = new ModelBuilder();
|
|
|
|
|
|
|
|
var User1 = ds.define('User', {}, {idInjection: false});
|
|
|
|
assert(!User1.definition.properties.id);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('updates generated id type by the connector', function (done) {
|
|
|
|
var builder = new ModelBuilder();
|
|
|
|
|
|
|
|
var User = builder.define('User', {id: {type: String, generated: true, id: true}});
|
|
|
|
assert.deepEqual(User.definition.properties.id,
|
|
|
|
{type: String, id: 1, generated: true});
|
|
|
|
|
|
|
|
var ds = new DataSource('memory');// define models
|
|
|
|
User.attachTo(ds);
|
|
|
|
|
|
|
|
assert.deepEqual(User.definition.properties.id,
|
|
|
|
{type: Number, id: 1, generated: true});
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
2013-05-28 20:50:59 +00:00
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2013-11-08 01:11:17 +00:00
|
|
|
describe('Load models with base', function () {
|
|
|
|
it('should set up base class', function (done) {
|
|
|
|
var ds = new ModelBuilder();
|
|
|
|
|
|
|
|
var 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
|
|
|
|
2013-11-08 01:11:17 +00:00
|
|
|
var Customer = ds.define('Customer', {vip: Boolean}, {base: 'User'});
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-11-08 01:11:17 +00:00
|
|
|
try {
|
|
|
|
var 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-28 22:23:48 +00:00
|
|
|
describe('DataSource connector types', function() {
|
|
|
|
it('should return an array of types', function() {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var types = ds.getTypes();
|
|
|
|
assert.deepEqual(types, ['db', 'nosql', 'memory']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should test supported types by string', function() {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var result = ds.supportTypes('db');
|
|
|
|
assert(result);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should test supported types by array', function() {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var result = ds.supportTypes(['db', 'memory']);
|
|
|
|
assert(result);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should test unsupported types by string', function() {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var result = ds.supportTypes('rdbms');
|
|
|
|
assert(!result);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should test unsupported types by array', function() {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var result = ds.supportTypes(['rdbms', 'memory']);
|
|
|
|
assert(!result);
|
|
|
|
|
|
|
|
result = ds.supportTypes(['rdbms']);
|
|
|
|
assert(!result);
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
describe('DataSource constructor', function () {
|
2014-01-13 19:06:02 +00:00
|
|
|
// Mocked require
|
2014-01-24 17:09:53 +00:00
|
|
|
var loader = function (name) {
|
2014-01-13 19:06:02 +00:00
|
|
|
return {
|
|
|
|
name: name
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should resolve connector by path', function () {
|
2014-01-10 01:03:37 +00:00
|
|
|
var connector = DataSource._resolveConnector(__dirname + '/../lib/connectors/memory');
|
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should resolve connector by internal name', function () {
|
2014-01-10 01:03:37 +00:00
|
|
|
var connector = DataSource._resolveConnector('memory');
|
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should try to resolve connector by module name starts with loopback-connector-', function () {
|
2014-01-13 19:06:02 +00:00
|
|
|
var connector = DataSource._resolveConnector('loopback-connector-xyz', loader);
|
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should try to resolve connector by short module name', function () {
|
2014-01-13 19:06:02 +00:00
|
|
|
var connector = DataSource._resolveConnector('xyz', loader);
|
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should try to resolve connector by full module name', function () {
|
2014-01-13 19:06:02 +00:00
|
|
|
var connector = DataSource._resolveConnector('loopback-xyz', loader);
|
|
|
|
assert(connector.connector);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should fail to resolve connector by module name starts with loopback-connector-', function () {
|
2014-01-10 01:03:37 +00:00
|
|
|
var connector = DataSource._resolveConnector('loopback-connector-xyz');
|
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-xyz') !== -1);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should fail to resolve connector by short module name', function () {
|
2014-01-10 01:03:37 +00:00
|
|
|
var connector = DataSource._resolveConnector('xyz');
|
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-xyz') !== -1);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should fail to resolve connector by full module name', function () {
|
2014-01-10 01:03:37 +00:00
|
|
|
var connector = DataSource._resolveConnector('loopback-xyz');
|
|
|
|
assert(!connector.connector);
|
|
|
|
assert(connector.error.indexOf('loopback-connector-loopback-xyz') !== -1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-11-05 06:53:02 +00:00
|
|
|
describe('Load models with relations', function () {
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should set up relations', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Post = ds.define('Post', {userId: Number, content: String});
|
|
|
|
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}}});
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should set up belongsTo relations', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {name: String});
|
|
|
|
var Post = ds.define('Post', {userId: Number, content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(Post.relations['user']);
|
|
|
|
done();
|
|
|
|
});
|
2014-01-21 17:47:32 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should set up foreign key with the correct type', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2014-01-21 17:47:32 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {name: String, id: {type: String, id: true}});
|
|
|
|
var Post = ds.define('Post', {content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
2014-01-21 17:47:32 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var fk = Post.definition.properties['userId'];
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should set up hasMany and belongsTo relations', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}, accounts: {type: 'hasMany', model: 'Account'}}});
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Post = ds.define('Post', {userId: Number, content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Account = ds.define('Account', {userId: Number, type: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert(Post.relations['user']);
|
|
|
|
assert.deepEqual(Post.relations['user'], {
|
|
|
|
type: 'belongsTo',
|
|
|
|
keyFrom: 'userId',
|
|
|
|
keyTo: 'id',
|
|
|
|
modelTo: User,
|
|
|
|
multiple: false
|
|
|
|
});
|
|
|
|
assert(User.relations['posts']);
|
|
|
|
assert.deepEqual(User.relations['posts'], {
|
|
|
|
type: 'hasMany',
|
|
|
|
keyFrom: 'id',
|
|
|
|
keyTo: 'userId',
|
|
|
|
modelTo: Post,
|
|
|
|
multiple: true
|
|
|
|
});
|
|
|
|
assert(User.relations['accounts']);
|
|
|
|
assert.deepEqual(User.relations['accounts'], {
|
|
|
|
type: 'hasMany',
|
|
|
|
keyFrom: 'id',
|
|
|
|
keyTo: 'userId',
|
|
|
|
modelTo: Account,
|
|
|
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should throw if a relation is missing type', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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 {
|
|
|
|
var User = ds.define('User', {name: String}, {relations: {posts: {model: 'Post'}}});
|
|
|
|
} catch (e) {
|
|
|
|
done();
|
|
|
|
}
|
2013-11-05 06:53:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should throw if the relation type is invalid', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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 {
|
|
|
|
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasXYZ', model: 'Post'}}});
|
|
|
|
} catch (e) {
|
|
|
|
done();
|
|
|
|
}
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should handle hasMany through', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var Physician = ds.createModel('Physician', {
|
|
|
|
name: String
|
|
|
|
}, {relations: {patients: {model: 'Patient', type: 'hasMany', through: 'Appointment'}}});
|
2013-11-05 17:29:24 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Patient = ds.createModel('Patient', {
|
|
|
|
name: String
|
|
|
|
}, {relations: {physicians: {model: 'Physician', type: 'hasMany', through: 'Appointment'}}});
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Appointment = ds.createModel('Appointment', {
|
|
|
|
physicianId: Number,
|
|
|
|
patientId: Number,
|
|
|
|
appointmentDate: Date
|
|
|
|
}, {relations: {patient: {type: 'belongsTo', model: 'Patient'}, physician: {type: 'belongsTo', model: 'Physician'}}});
|
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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should set up relations after attach', function (done) {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-11-07 21:28:18 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Post = modelBuilder.define('Post', {userId: Number, content: String});
|
|
|
|
var User = modelBuilder.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}}});
|
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 17:29:24 +00:00
|
|
|
|
2013-11-05 06:53:02 +00:00
|
|
|
});
|
|
|
|
|
2013-12-04 05:27:46 +00:00
|
|
|
describe('DataAccessObject', function () {
|
2013-12-04 18:00:33 +00:00
|
|
|
var ds, model, where;
|
|
|
|
|
|
|
|
before(function () {
|
|
|
|
ds = new DataSource('memory');
|
|
|
|
model = ds.createModel('M1', {
|
2013-12-04 05:27:46 +00:00
|
|
|
id: {type: String, id: true},
|
2013-12-04 18:00:33 +00:00
|
|
|
age: Number,
|
|
|
|
vip: Boolean,
|
|
|
|
date: Date,
|
2013-12-14 17:49:11 +00:00
|
|
|
location: 'GeoPoint',
|
2013-12-04 18:00:33 +00:00
|
|
|
scores: [Number]
|
2013-12-04 05:27:46 +00:00
|
|
|
});
|
2013-12-04 18:00:33 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to coerce where clause for string types', function () {
|
|
|
|
where = model._coerce({id: 1});
|
2013-12-04 05:27:46 +00:00
|
|
|
assert.deepEqual(where, {id: '1'});
|
2013-12-04 18:00:33 +00:00
|
|
|
where = model._coerce({id: '1'});
|
|
|
|
assert.deepEqual(where, {id: '1'});
|
|
|
|
});
|
2013-12-04 05:27:46 +00:00
|
|
|
|
2013-12-04 18:00:33 +00:00
|
|
|
it('should be able to coerce where clause for number types', function () {
|
2013-12-04 05:27:46 +00:00
|
|
|
where = model._coerce({age: '10'});
|
|
|
|
assert.deepEqual(where, {age: 10});
|
|
|
|
|
|
|
|
where = model._coerce({age: 10});
|
|
|
|
assert.deepEqual(where, {age: 10});
|
|
|
|
|
|
|
|
where = model._coerce({age: {gt: 10}});
|
|
|
|
assert.deepEqual(where, {age: {gt: 10}});
|
|
|
|
|
|
|
|
where = model._coerce({age: {gt: '10'}});
|
|
|
|
assert.deepEqual(where, {age: {gt: 10}});
|
|
|
|
|
|
|
|
where = model._coerce({age: {between: ['10', '20']}});
|
|
|
|
assert.deepEqual(where, {age: {between: [10, 20]}});
|
|
|
|
});
|
2013-12-04 18:00:33 +00:00
|
|
|
|
|
|
|
it('should be able to coerce where clause for array types', function () {
|
|
|
|
where = model._coerce({scores: ['10', '20']});
|
|
|
|
assert.deepEqual(where, {scores: [10, 20]});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to coerce where clause for date types', function () {
|
|
|
|
var d = new Date();
|
|
|
|
where = model._coerce({date: d});
|
|
|
|
assert.deepEqual(where, {date: d});
|
|
|
|
|
|
|
|
where = model._coerce({date: d.toISOString()});
|
|
|
|
assert.deepEqual(where, {date: d});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to coerce where clause for boolean types', function () {
|
|
|
|
where = model._coerce({vip: 'true'});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
|
|
|
|
|
|
|
where = model._coerce({vip: true});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
|
|
|
|
|
|
|
where = model._coerce({vip: 'false'});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
|
|
|
|
|
|
|
where = model._coerce({vip: false});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
|
|
|
|
|
|
|
where = model._coerce({vip: '1'});
|
|
|
|
assert.deepEqual(where, {vip: true});
|
|
|
|
|
|
|
|
where = model._coerce({vip: 0});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
|
|
|
|
|
|
|
where = model._coerce({vip: ''});
|
|
|
|
assert.deepEqual(where, {vip: false});
|
|
|
|
|
|
|
|
});
|
2013-12-14 17:49:11 +00:00
|
|
|
|
|
|
|
it('should skip GeoPoint', function () {
|
|
|
|
where = model._coerce({location: {near: {lng: 10, lat: 20}, maxDistance: 20}});
|
|
|
|
assert.deepEqual(where, {location: {near: {lng: 10, lat: 20}, maxDistance: 20}});
|
|
|
|
});
|
|
|
|
|
2013-12-16 16:36:15 +00:00
|
|
|
it('should skip null values', function () {
|
2013-12-14 17:54:28 +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
|
|
|
|
2013-12-16 16:36:15 +00:00
|
|
|
it('should skip undefined values', function () {
|
2013-12-14 17:54:28 +00:00
|
|
|
where = model._coerce({date: undefined});
|
|
|
|
assert.deepEqual(where, {date: undefined});
|
|
|
|
});
|
|
|
|
|
2013-12-16 16:36:15 +00:00
|
|
|
it('should skip conversion if a simple property produces NaN for numbers',
|
|
|
|
function () {
|
2014-01-24 17:09:53 +00:00
|
|
|
where = model._coerce({age: 'xyz'});
|
|
|
|
assert.deepEqual(where, {age: 'xyz'});
|
|
|
|
});
|
2013-12-16 06:51:47 +00:00
|
|
|
|
2013-12-16 16:36:15 +00:00
|
|
|
it('should skip conversion if an array property produces NaN for numbers',
|
|
|
|
function () {
|
2014-01-24 17:09:53 +00:00
|
|
|
where = model._coerce({age: {inq: ['xyz', '12']}});
|
|
|
|
assert.deepEqual(where, {age: {inq: ['xyz', 12]}});
|
|
|
|
});
|
2013-12-16 06:51:47 +00:00
|
|
|
|
2013-12-04 05:27:46 +00:00
|
|
|
});
|
|
|
|
|
2013-05-28 22:26:12 +00:00
|
|
|
describe('Load models from json', function () {
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to define models from json', function () {
|
|
|
|
var path = require('path'),
|
|
|
|
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) {
|
|
|
|
// Set up the data source
|
|
|
|
if (!dataSource) {
|
|
|
|
dataSource = new DataSource('memory');
|
|
|
|
}
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// Read the dataSource JSON file
|
|
|
|
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
return dataSource.modelBuilder.buildModels(schemas);
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var 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
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var m1 = new models.AnonymousModel_0({title: 'Test'});
|
|
|
|
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');
|
|
|
|
for (var s in models) {
|
|
|
|
var m = models[s];
|
|
|
|
assert(new m());
|
|
|
|
}
|
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should be able to extend models', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var User = modelBuilder.define('User', {
|
|
|
|
name: String,
|
|
|
|
bio: ModelBuilder.Text,
|
|
|
|
approved: Boolean,
|
|
|
|
joinedAt: Date,
|
|
|
|
age: Number
|
2013-05-28 22:26:12 +00:00
|
|
|
});
|
2013-10-07 04:13:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Customer = User.extend('Customer', {customerId: {type: String, id: true}});
|
2013-10-07 04:13:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var customer = new Customer({name: 'Joe', age: 20, customerId: 'c01'});
|
2013-10-07 04:13:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
customer.should.be.a('object').and.have.property('name', 'Joe');
|
|
|
|
customer.should.have.property('name', 'Joe');
|
|
|
|
customer.should.have.property('age', 20);
|
|
|
|
customer.should.have.property('customerId', 'c01');
|
|
|
|
customer.should.not.have.property('bio');
|
2013-10-07 04:13:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// The properties are defined at prototype level
|
|
|
|
assert.equal(Object.keys(customer).length, 0);
|
|
|
|
var count = 0;
|
|
|
|
for (var p in customer) {
|
|
|
|
if (typeof customer[p] !== 'function') {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.equal(count, 7); // Please note there is an injected id from User prototype
|
|
|
|
assert.equal(Object.keys(customer.toObject()).length, 6);
|
2013-10-07 04:13:52 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
done(null, customer);
|
|
|
|
});
|
2013-12-06 23:52:39 +00:00
|
|
|
|
|
|
|
it('should be able to extend models with merged settings', function (done) {
|
|
|
|
var modelBuilder = new ModelBuilder();
|
|
|
|
|
|
|
|
var User = modelBuilder.define('User', {
|
|
|
|
name: String
|
|
|
|
}, {
|
|
|
|
defaultPermission: 'ALLOW',
|
|
|
|
acls: [
|
|
|
|
{
|
|
|
|
principalType: 'ROLE',
|
|
|
|
principalId: '$everyone',
|
|
|
|
permission: 'ALLOW'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
relations: {
|
|
|
|
posts: {
|
|
|
|
type: 'hasMany',
|
2014-01-24 17:09:53 +00:00
|
|
|
model: 'Post'
|
2013-12-06 23:52:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Customer = User.extend('Customer',
|
|
|
|
{customerId: {type: String, id: true}},
|
|
|
|
{
|
2013-12-06 23:52:39 +00:00
|
|
|
defaultPermission: 'DENY',
|
|
|
|
acls: [
|
|
|
|
{
|
|
|
|
principalType: 'ROLE',
|
|
|
|
principalId: '$unauthenticated',
|
|
|
|
permission: 'DENY'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
relations: {
|
|
|
|
orders: {
|
|
|
|
type: 'hasMany',
|
2014-01-24 17:09:53 +00:00
|
|
|
model: 'Order'
|
2013-12-06 23:52:39 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2013-12-06 23:52:39 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.deepEqual(User.settings, {
|
|
|
|
defaultPermission: 'ALLOW',
|
|
|
|
acls: [
|
|
|
|
{
|
|
|
|
principalType: 'ROLE',
|
|
|
|
principalId: '$everyone',
|
|
|
|
permission: 'ALLOW'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
relations: {
|
|
|
|
posts: {
|
|
|
|
type: 'hasMany',
|
|
|
|
model: 'Post'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
strict: false
|
2013-12-06 23:52:39 +00:00
|
|
|
});
|
2013-05-28 22:26:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
assert.deepEqual(Customer.settings, {
|
|
|
|
defaultPermission: 'DENY',
|
|
|
|
acls: [
|
|
|
|
{
|
|
|
|
principalType: 'ROLE',
|
|
|
|
principalId: '$everyone',
|
|
|
|
permission: 'ALLOW'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
principalType: 'ROLE',
|
|
|
|
principalId: '$unauthenticated',
|
|
|
|
permission: 'DENY'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
relations: {
|
|
|
|
posts: {
|
|
|
|
type: 'hasMany',
|
|
|
|
model: 'Post'
|
|
|
|
},
|
|
|
|
orders: {
|
|
|
|
type: 'hasMany',
|
|
|
|
model: 'Order'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
strict: false
|
2013-10-25 23:18:02 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
describe('DataSource constructor', function () {
|
|
|
|
it('Takes url as the settings', function () {
|
|
|
|
var ds = new DataSource('memory://localhost/mydb?x=1');
|
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('Takes connector name', function () {
|
|
|
|
var ds = new DataSource('memory');
|
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
2013-10-25 23:18:02 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('Takes settings object', function () {
|
|
|
|
var ds = new DataSource({connector: 'memory'});
|
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Takes settings object and name', function () {
|
|
|
|
var ds = new DataSource('x', {connector: 'memory'});
|
|
|
|
assert.equal(ds.connector.name, 'memory');
|
|
|
|
});
|
|
|
|
});
|
2013-12-04 05:14:12 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
describe('Injected methods from connectors', function () {
|
|
|
|
it('are not shared across models for remote methods', function () {
|
2013-12-04 05:14:12 +00:00
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var M1 = ds.createModel('M1');
|
|
|
|
var M2 = ds.createModel('M2');
|
|
|
|
// Remotable methods are not shared across models
|
|
|
|
assert.notEqual(M1.create, M2.create, 'Remotable methods are not shared');
|
|
|
|
assert.equal(M1.create.shared, true, 'M1.create is remotable');
|
|
|
|
assert.equal(M2.create.shared, true, 'M2.create is remotable');
|
|
|
|
M1.create.shared = false;
|
|
|
|
assert.equal(M1.create.shared, false, 'M1.create should be local now');
|
|
|
|
assert.equal(M2.create.shared, true, 'M2.create should stay remotable');
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('are not shared across models for non-remote methods', function () {
|
2013-12-05 23:19:40 +00:00
|
|
|
var ds = new DataSource('memory');
|
|
|
|
var M1 = ds.createModel('M1');
|
|
|
|
var M2 = ds.createModel('M2');
|
|
|
|
var m1 = M1.prototype.save;
|
|
|
|
var m2 = M2.prototype.save;
|
|
|
|
assert.notEqual(m1, m2, 'non-remote methods are not shared');
|
|
|
|
assert.equal(!!m1.shared, false, 'M1.save is not remotable');
|
|
|
|
assert.equal(!!m2.shared, false, 'M2.save is not remotable');
|
|
|
|
m1.shared = true;
|
|
|
|
assert.equal(m1.shared, true, 'M1.save is now remotable');
|
|
|
|
assert.equal(!!m2.shared, false, 'M2.save is not remotable');
|
|
|
|
|
2013-12-14 00:45:31 +00:00
|
|
|
assert.equal(M1.deleteById, M1.removeById,
|
|
|
|
'Same methods on the same model should have the same proxy');
|
|
|
|
|
|
|
|
assert.notEqual(M1.deleteById, M2.deleteById,
|
|
|
|
'Same methods on differnt models should have different proxies');
|
|
|
|
|
2013-12-05 23:19:40 +00:00
|
|
|
});
|
2013-12-04 05:14:12 +00:00
|
|
|
|
|
|
|
});
|
2013-12-19 00:13:41 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
describe('ModelBuilder options.models', function () {
|
|
|
|
it('should inject model classes from models', function () {
|
2013-12-19 00:13:41 +00:00
|
|
|
var builder = new ModelBuilder();
|
|
|
|
var M1 = builder.define('M1');
|
|
|
|
var M2 = builder.define('M2', {}, {models: {
|
|
|
|
'M1': M1
|
|
|
|
}});
|
|
|
|
|
|
|
|
assert.equal(M2.M1, M1, 'M1 should be injected to M2');
|
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
it('should inject model classes by name in the models', function () {
|
2013-12-19 00:13:41 +00:00
|
|
|
var builder = new ModelBuilder();
|
|
|
|
var M1 = builder.define('M1');
|
|
|
|
var M2 = builder.define('M2', {}, {models: {
|
|
|
|
'M1': 'M1'
|
|
|
|
}});
|
|
|
|
|
|
|
|
assert.equal(M2.M1, M1, 'M1 should be injected to M2');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject model classes by name in the models before the class is defined',
|
2014-01-24 17:09:53 +00:00
|
|
|
function () {
|
2013-12-19 00:13:41 +00:00
|
|
|
var builder = new ModelBuilder();
|
|
|
|
var M2 = builder.define('M2', {}, {models: {
|
|
|
|
'M1': 'M1'
|
|
|
|
}});
|
|
|
|
assert(M2.M1, 'M1 should be injected to M2');
|
|
|
|
assert(M2.M1.settings.unresolved, 'M1 is still a proxy');
|
|
|
|
var M1 = builder.define('M1');
|
|
|
|
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
|
|
|
|
2013-12-19 01:14:54 +00:00
|
|
|
});
|
|
|
|
|