Clean up tests

This commit is contained in:
Geoffroy 2016-01-07 00:45:29 -06:00 committed by Simon Ho
parent 7ac9679c17
commit ba1703fbea
10 changed files with 526 additions and 370 deletions

View File

@ -14,8 +14,9 @@
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"unused": false,
"trailing": true,
"sub": true,
"maxlen": 80
"maxlen": 80,
"predef": [ "describe", "beforeEach", "afterEach", "before", "after", "it" ]
}

View File

@ -7,7 +7,8 @@ module.exports = function(grunt) {
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
'<%= pkg.author.name %>;' +
' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
// Task configuration.
jshint: {
@ -17,7 +18,7 @@ module.exports = function(grunt) {
gruntfile: {
src: 'Gruntfile.js'
},
lib_test: {
libTest: {
src: ['lib/**/*.js', 'test/**/*.js']
}
},

View File

@ -41,14 +41,15 @@ function RelationMixin() {
* // Save the new chapter
* chapter.save();
*
* // you can also call the Chapter.create method with the `chapters` property which will build a chapter
* // instance and save the it in the data source.
* // you can also call the Chapter.create method with the `chapters` property
* // which will build a chapter instance and save the it in the data source.
* book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
* // this callback is optional
* });
*
* // Query chapters for the book
* book.chapters(function(err, chapters) { // all chapters with bookId = book.id
* book.chapters(function(err, chapters) {
* // all chapters with bookId = book.id
* console.log(chapters);
* });
*
@ -58,9 +59,11 @@ function RelationMixin() {
* });
* });
*```
* @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @param {Object|String} modelTo Model object (or String name of model) to
* which you are creating the relationship.
* @options {Object} parameters Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} as Name of the property in the referring model that
* corresponds to the foreign key field in the related model.
* @property {String} foreignKey Property name of foreign key field.
* @property {Object} model Model object
*/
@ -71,12 +74,14 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
};
/**
* Declare "belongsTo" relation that sets up a one-to-one connection with another model, such that each
* instance of the declaring model "belongs to" one instance of the other model.
* Declare "belongsTo" relation that sets up a one-to-one connection with
* another model, such that each instance of the declaring model "belongs
* to" one instance of the other model.
*
* For example, if an application includes users and posts, and each post can be written by exactly one user.
* The following code specifies that `Post` has a reference called `author` to the `User` model via the `userId` property of `Post`
* as the foreign key.
* For example, if an application includes users and posts, and each post can be
* written by exactly one user. The following code specifies that `Post` has a
* reference called `author` to the `User` model via the `userId` property of
* `Post` as the foreign key.
* ```
* Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
* ```
@ -94,7 +99,8 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
* ```
* Examples:
*
* Suppose the model Post has a *belongsTo* relationship with User (the author of the post). You could declare it this way:
* Suppose the model Post has a *belongsTo* relationship with User (the author
* of the post). You could declare it this way:
* ```js
* Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
* ```
@ -106,18 +112,22 @@ RelationMixin.hasMany = function hasMany(modelTo, params) {
* });
* ```
*
* The related object is cached, so if later you try to get again the author, no additional request will be made.
* But there is an optional boolean parameter in first position that set whether or not you want to reload the cache:
* The related object is cached, so if later you try to get again the author, no
* additional request will be made. But there is an optional boolean parameter
* in first position that set whether or not you want to reload the cache:
* ```js
* post.author(true, function(err, user) {
* // The user is reloaded, even if it was already cached.
* });
* ```
* This optional parameter default value is false, so the related object will be loaded from cache if available.
* This optional parameter default value is false, so the related object will
* be loaded from cache if available.
*
* @param {Class|String} modelTo Model object (or String name of model) to which you are creating the relationship.
* @param {Class|String} modelTo Model object (or String name of model)
* to which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} as Name of the property in the referring model that
* corresponds to the foreign key field in the related model.
* @property {String} foreignKey Name of foreign key property.
*
*/
@ -128,9 +138,10 @@ RelationMixin.belongsTo = function(modelTo, params) {
};
/**
* A hasAndBelongsToMany relation creates a direct many-to-many connection with another model, with no intervening model.
* For example, if your application includes users and groups, with each group having many users and each user appearing
* in many groups, you could declare the models this way:
* A hasAndBelongsToMany relation creates a direct many-to-many connection with
* another model, with no intervening model. For example, if your application
* includes users and groups, with each group having many users and each user
* appearing in many groups, you could declare the models this way:
* ```
* User.hasAndBelongsToMany('groups', {model: Group, foreignKey: 'groupId'});
* ```
@ -151,10 +162,11 @@ RelationMixin.belongsTo = function(modelTo, params) {
* user.groups.remove(group, callback);
* ```
*
* @param {String|Object} modelTo Model object (or String name of model) to which you are creating the relationship.
* the relation
* @param {String|Object} modelTo Model object (or String name of model) to
* which you are creating the relationship.
* @options {Object} params Configuration parameters; see below.
* @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
* @property {String} as Name of the property in the referring model that
* corresponds to the foreign key field in the related model.
* @property {String} foreignKey Property name of foreign key field.
* @property {Object} model Model object
*/
@ -202,7 +214,7 @@ function defineRelationProperty(modelClass, def) {
scope.create = function() {
return that['__create__' + def.name].apply(that, arguments);
};
scope.deleteById = destroyById = function() {
scope.deleteById = scope.destroyById = function() {
return that['__destroyById__' + def.name].apply(that, arguments);
};
scope.exists = function() {

View File

@ -24,7 +24,7 @@ function RemoteConnector(settings) {
'cannot initiaze RemoteConnector without a settings object');
this.client = settings.client;
this.adapter = settings.adapter || 'rest';
this.protocol = settings.protocol || 'http'
this.protocol = settings.protocol || 'http';
this.root = settings.root || '';
this.host = settings.host || 'localhost';
this.port = settings.port || 3000;

View File

@ -32,6 +32,7 @@
"devDependencies": {
"assert": "^1.1.2",
"grunt": "~0.4.5",
"grunt-cli": "^0.1.13",
"grunt-contrib-jshint": "~0.10.0",
"grunt-mocha-test": "^0.11.0",
"loopback": "^2.2.0",

55
test/helper.js Normal file
View File

@ -0,0 +1,55 @@
var loopback = require('loopback');
var remoteConnector = require('..');
exports.createMemoryDataSource = createMemoryDataSource;
exports.createModel = createModel;
exports.createRemoteDataSource = createRemoteDataSource;
exports.createRestAppAndListen = createRestAppAndListen;
exports.getUserProperties = getUserProperties;
function createRestAppAndListen(port) {
var app = loopback();
app.set('host', '127.0.0.1');
if (port) app.set('port', port);
app.use(loopback.rest());
app.locals.handler = app.listen();
return app;
}
function createMemoryDataSource() {
return loopback.createDataSource({connector: 'memory'});
}
function createRemoteDataSource(remoteApp) {
return loopback.createDataSource({
url: 'http://' + remoteApp.get('host') + ':' + remoteApp.get('port'),
connector: remoteConnector
});
}
/**
* Used to create models based on a set of options. May associate or link to an
* app.
*/
function createModel(options) {
var Model = loopback.PersistedModel.extend(options.parent, options.properties,
options.options);
if (options.app) options.app.model(Model);
if (options.datasource) Model.attachTo(options.datasource);
return Model;
}
function getUserProperties() {
return {
'first': String,
'last': String,
'age': Number,
'password': String,
'gender': String,
'domain': String,
'email': String
};
}

218
test/models.test.js Normal file
View File

@ -0,0 +1,218 @@
var assert = require('assert');
var helper = require('./helper');
var TaskEmitter = require('strong-task-emitter');
describe('Model tests', function() {
var User;
beforeEach(function() {
User = helper.createModel({
parent: 'user',
app: helper.createRestAppAndListen(),
datasource: helper.createMemoryDataSource(),
properties: helper.getUserProperties()
});
});
describe('Model.validatesPresenceOf(properties...)', function() {
it('should require a model to include a property to be considered valid',
function() {
User.validatesPresenceOf('first', 'last', 'age');
var joe = new User({first: 'joe'});
assert(joe.isValid() === false, 'model should not validate');
assert(joe.errors.last, 'should have a missing last error');
assert(joe.errors.age, 'should have a missing age error');
});
});
describe('Model.validatesLengthOf(property, options)', function() {
it('should require a property length to be within a specified range',
function() {
User.validatesLengthOf('password', {min: 5, message: {min:
'Password is too short'}});
var joe = new User({password: '1234'});
assert(joe.isValid() === false, 'model should not be valid');
assert(joe.errors.password, 'should have password error');
});
});
describe('Model.validatesInclusionOf(property, options)', function() {
it('should require a value for `property` to be in the specified array',
function() {
User.validatesInclusionOf('gender', {in: ['male', 'female']});
var foo = new User({gender: 'bar'});
assert(foo.isValid() === false, 'model should not be valid');
assert(foo.errors.gender, 'should have gender error');
});
});
describe('Model.validatesExclusionOf(property, options)', function() {
it('should require a value for `property` to not exist in the specified ' +
'array', function() {
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
var foo = new User({domain: 'www'});
var bar = new User({domain: 'billing'});
var bat = new User({domain: 'admin'});
assert(foo.isValid() === false);
assert(bar.isValid() === false);
assert(bat.isValid() === false);
assert(foo.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
});
});
describe('Model.validatesNumericalityOf(property, options)', function() {
it('should require a value for `property` to be a specific type of ' +
'`Number`', function() {
User.validatesNumericalityOf('age', {int: true});
var joe = new User({age: 10.2});
assert(joe.isValid() === false);
var bob = new User({age: 0});
assert(bob.isValid() === true);
assert(joe.errors.age, 'model should have an age error');
});
});
describe('myModel.isValid()', function() {
it('should validate the model instance', function() {
User.validatesNumericalityOf('age', {int: true});
var user = new User({first: 'joe', age: 'flarg'});
var valid = user.isValid();
assert(valid === false);
assert(user.errors.age, 'model should have age error');
});
it('should validate the model asynchronously', function(done) {
User.validatesNumericalityOf('age', {int: true});
var user = new User({first: 'joe', age: 'flarg'});
user.isValid(function(valid) {
assert(valid === false);
assert(user.errors.age, 'model should have age error');
done();
});
});
});
describe('Model.create([data], [callback])', function() {
it('should create an instance and save to the attached data source',
function(done) {
User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
assert(user instanceof User);
done();
});
});
});
describe('model.save([options], [callback])', function() {
it('should save an instance of a Model to the attached data source',
function(done) {
var joe = new User({first: 'Joe', last: 'Bob'});
joe.save(function(err, user) {
assert(user.id);
assert(!err);
assert(!user.errors);
done();
});
});
});
describe('model.updateAttributes(data, [callback])', function() {
it('should save specified attributes to the attached data source',
function(done) {
User.create({first: 'joe', age: 100}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
user.updateAttributes({
first: 'updatedFirst',
last: 'updatedLast'
}, function(err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'updatedFirst');
assert.equal(updatedUser.last, 'updatedLast');
assert.equal(updatedUser.age, 100);
done();
});
});
});
});
describe('Model.upsert(data, callback)', function() {
it('should update when a record with id=data.id is found, insert otherwise',
function(done) {
User.upsert({first: 'joe', id: 7}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
User.upsert({first: 'bob', id: 7}, function(err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'bob');
done();
});
});
});
});
describe('model.destroy([callback])', function() {
it('should remove a model from the attached data source', function(done) {
User.create({first: 'joe', last: 'bob'}, function(err, user) {
User.findById(user.id, function(err, foundUser) {
assert.equal(user.id, foundUser.id);
foundUser.destroy(function() {
User.findById(user.id, function(err, notFound) {
assert.equal(notFound, null);
done();
});
});
});
});
});
});
describe('Model.deleteById(id, [callback])', function() {
it('should delete a model instance from the attached data source',
function(done) {
User.create({first: 'joe', last: 'bob'}, function(err, user) {
User.deleteById(user.id, function(err) {
User.findById(user.id, function(err, notFound) {
assert.equal(notFound, null);
done();
});
});
});
});
});
describe('Model.findById(id, callback)', function() {
it('should find an instance by id', function(done) {
User.create({first: 'michael', last: 'jordan', id: 23}, function() {
User.findById(23, function(err, user) {
assert.equal(user.id, 23);
assert.equal(user.first, 'michael');
assert.equal(user.last, 'jordan');
done();
});
});
});
});
describe('Model.count([query], callback)', function() {
it('should return the count of Model instances in data source',
function(done) {
var taskEmitter = new TaskEmitter();
taskEmitter
.task(User, 'create', {first: 'jill', age: 100})
.task(User, 'create', {first: 'bob', age: 200})
.task(User, 'create', {first: 'jan'})
.task(User, 'create', {first: 'sam'})
.task(User, 'create', {first: 'suzy'})
.on('done', function() {
User.count({age: {gt: 99}}, function(err, count) {
assert.equal(count, 2);
done();
});
});
});
});
});

View File

@ -1,123 +1,109 @@
var loopback = require('loopback');
var defineModelTestsWithDataSource = require('./util/model-tests');
var assert = require('assert');
var Remote = require('..');
function createAppWithRest() {
var app = loopback();
app.set('host', '127.0.0.1');
app.use(loopback.rest());
return app;
}
function listenAndSetupRemoteDS(test, app, remoteName, cb) {
app.listen(0, function() {
test[remoteName] = loopback.createDataSource({
host: '127.0.0.1',
port: app.get('port'),
connector: Remote,
});
cb();
});
}
var helper = require('./helper');
describe('RemoteConnector', function() {
var remoteApp;
var remote;
var ctx = this;
defineModelTestsWithDataSource({
beforeEach: function(done) {
var test = this;
remoteApp = createAppWithRest();
listenAndSetupRemoteDS(test, remoteApp, 'dataSource', done);
},
onDefine: function(Model) {
var RemoteModel = Model.extend(Model.modelName);
RemoteModel.attachTo(loopback.createDataSource({
connector: loopback.Memory
}));
remoteApp.model(RemoteModel);
}
before(function() {
ctx.serverApp = helper.createRestAppAndListen(3001);
ctx.ServerModel = helper.createModel({
parent: 'TestModel',
app: ctx.serverApp,
datasource: helper.createMemoryDataSource()
});
ctx.remoteApp = helper.createRestAppAndListen(3002);
ctx.RemoteModel = helper.createModel({
parent: 'TestModel',
app: ctx.remoteApp,
datasource: helper.createRemoteDataSource(ctx.serverApp)
});
});
beforeEach(function(done) {
var test = this;
var ServerModel = this.ServerModel =
loopback.PersistedModel.extend('TestModel');
remoteApp = test.remoteApp = createAppWithRest();
remoteApp.model(ServerModel);
listenAndSetupRemoteDS(test, remoteApp, 'remote', done);
after(function() {
ctx.serverApp.locals.handler.close();
ctx.remoteApp.locals.handler.close();
ctx.ServerModel = null;
ctx.RemoteModel = null;
});
it('should support the save method', function(done) {
var calledServerCreate = false;
var RemoteModel = loopback.PersistedModel.extend('TestModel');
RemoteModel.attachTo(this.remote);
var ServerModel = this.ServerModel;
ServerModel.create = function(data, cb) {
ctx.ServerModel.create = function(data, cb, callback) {
calledServerCreate = true;
data.id = 1;
cb(null, data);
}
if (callback) callback(null, data);
else cb(null, data);
};
ServerModel.setupRemoting();
var m = new RemoteModel({foo: 'bar'});
m.save(function(err, inst) {
assert(inst instanceof RemoteModel);
var m = new ctx.RemoteModel({foo: 'bar'});
m.save(function(err, instance) {
if (err) return done(err);
assert(instance);
assert(instance instanceof ctx.RemoteModel);
assert(calledServerCreate);
done();
});
});
it('should support aliases', function(done) {
var RemoteModel = loopback.PersistedModel.extend('TestModel');
RemoteModel.attachTo(this.remote);
var ServerModel = this.ServerModel;
ServerModel.upsert = function(id, cb) {
done();
var calledServerUpsert = false;
ctx.ServerModel.upsert = function(id, cb) {
calledServerUpsert = true;
cb();
};
RemoteModel.updateOrCreate({}, function(err, inst) {
ctx.RemoteModel.updateOrCreate({}, function(err, instance) {
if (err) return done(err);
assert(instance);
assert(instance instanceof ctx.RemoteModel);
assert(calledServerUpsert);
done();
});
});
});
describe('Custom Path', function() {
var test = this;
var ctx = this;
before(function(done) {
var ServerModel = loopback.PersistedModel.extend('TestModel', {}, {
http: {path: '/custom'}
ctx.serverApp = helper.createRestAppAndListen(3001);
ctx.ServerModel = helper.createModel({
parent: 'TestModel',
app: ctx.serverApp,
datasource: helper.createMemoryDataSource(),
options: {
http: {path: '/custom'}
}
});
server = test.server = createAppWithRest();
server.dataSource('db', {
connector: loopback.Memory,
name: 'db'
ctx.remoteApp = helper.createRestAppAndListen(3002);
ctx.RemoteModel = helper.createModel({
parent: 'TestModel',
app: ctx.remoteApp,
datasource: helper.createRemoteDataSource(ctx.serverApp),
options: {
dataSource: 'remote',
http: {path: '/custom'}
}
});
server.model(ServerModel, {dataSource: 'db'});
done();
});
listenAndSetupRemoteDS(test, server, 'remote', done);
after(function(done)
{
ctx.serverApp.locals.handler.close();
ctx.remoteApp.locals.handler.close();
ctx.ServerModel = null;
ctx.RemoteModel = null;
done();
});
it('should support http.path configuration', function(done) {
var RemoteModel = loopback.PersistedModel.extend('TestModel', {}, {
dataSource: 'remote',
http: {path: '/custom'}
});
RemoteModel.attachTo(test.remote);
RemoteModel.create({}, function(err, instance) {
if (err) return assert(err);
ctx.RemoteModel.create({}, function(err, instance) {
if (err) return done(err);
assert(instance);
done();
});
});
});
});

141
test/remote-models.test.js Normal file
View File

@ -0,0 +1,141 @@
var assert = require('assert');
var helper = require('./helper');
var TaskEmitter = require('strong-task-emitter');
describe('Remote model tests', function() {
var ctx = this;
beforeEach(function() {
ctx.serverApp = helper.createRestAppAndListen(3001);
ctx.ServerModel = helper.createModel({
parent: 'TestModel',
app: ctx.serverApp,
datasource: helper.createMemoryDataSource(),
properties: helper.userProperties
});
ctx.remoteApp = helper.createRestAppAndListen(3002);
ctx.RemoteModel = helper.createModel({
parent: 'TestModel',
app: ctx.remoteApp,
datasource: helper.createRemoteDataSource(ctx.serverApp),
properties: helper.userProperties
});
});
afterEach(function() {
ctx.serverApp.locals.handler.close();
ctx.remoteApp.locals.handler.close();
ctx.ServerModel = null;
ctx.RemoteModel = null;
});
describe('Model.create([data], [callback])', function() {
it('should create an instance and save to the attached data source',
function(done) {
ctx.RemoteModel.create({first: 'Joe', last: 'Bob'}, function(err, user) {
assert(user instanceof ctx.RemoteModel);
done();
});
});
});
describe('model.save([options], [callback])', function() {
it('should save an instance of a Model to the attached data source',
function(done) {
var joe = new ctx.RemoteModel({first: 'Joe', last: 'Bob'});
joe.save(function(err, user) {
assert(user.id);
assert(!err);
assert(!user.errors);
done();
});
});
});
describe('model.updateAttributes(data, [callback])', function() {
it('should save specified attributes to the attached data source',
function(done) {
ctx.ServerModel.create({first: 'joe', age: 100}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
user.updateAttributes({
first: 'updatedFirst',
last: 'updatedLast'
}, function(err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'updatedFirst');
assert.equal(updatedUser.last, 'updatedLast');
assert.equal(updatedUser.age, 100);
done();
});
});
});
});
describe('Model.upsert(data, callback)', function() {
it('should update when a record with id=data.id is found, insert otherwise',
function(done) {
ctx.RemoteModel.upsert({first: 'joe', id: 7}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
ctx.RemoteModel.upsert({first: 'bob', id: 7}, function(err,
updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'bob');
done();
});
});
});
});
describe('Model.deleteById(id, [callback])', function() {
it('should delete a model instance from the attached data source',
function(done) {
ctx.ServerModel.create({first: 'joe', last: 'bob'}, function(err, user) {
ctx.RemoteModel.deleteById(user.id, function(err) {
ctx.RemoteModel.findById(user.id, function(err, notFound) {
assert.equal(notFound, null);
done();
});
});
});
});
});
describe('Model.findById(id, callback)', function() {
it('should find an instance by id from the attached data source',
function(done) {
ctx.ServerModel.create({first: 'michael', last: 'jordan', id: 23},
function() {
ctx.RemoteModel.findById(23, function(err, user) {
assert.equal(user.id, 23);
assert.equal(user.first, 'michael');
assert.equal(user.last, 'jordan');
done();
});
});
});
});
describe('Model.count([query], callback)', function() {
it('should return the count of Model instances from both data source',
function(done) {
var taskEmitter = new TaskEmitter();
taskEmitter
.task(ctx.ServerModel, 'create', {first: 'jill', age: 100})
.task(ctx.RemoteModel, 'create', {first: 'bob', age: 200})
.task(ctx.RemoteModel, 'create', {first: 'jan'})
.task(ctx.ServerModel, 'create', {first: 'sam'})
.task(ctx.ServerModel, 'create', {first: 'suzy'})
.on('done', function() {
ctx.RemoteModel.count({age: {gt: 99}}, function(err, count) {
assert.equal(count, 2);
done();
});
});
});
});
});

View File

@ -1,259 +0,0 @@
var assert = require('assert');
var loopback = require('loopback');
var PersistedModel = loopback.PersistedModel;
var TaskEmitter = require('strong-task-emitter');
module.exports = function defineModelTestsWithDataSource(options) {
describe('Model Tests', function() {
var User, dataSource;
if (options.beforeEach) {
beforeEach(options.beforeEach);
}
beforeEach(function() {
var test = this;
// setup a model / datasource
dataSource =
this.dataSource || loopback.createDataSource(options.dataSource);
var extend = PersistedModel.extend;
// create model hook
PersistedModel.extend = function() {
var extendedModel = extend.apply(PersistedModel, arguments);
if (options.onDefine) {
options.onDefine.call(test, extendedModel);
}
return extendedModel;
}
User = PersistedModel.extend('user', {
'first': String,
'last': String,
'age': Number,
'password': String,
'gender': String,
'domain': String,
'email': String
}, {
trackChanges: true
});
// enable destroy all for testing
User.destroyAll.shared = true;
User.attachTo(dataSource);
});
describe('Model.validatesPresenceOf(properties...)', function() {
it("Require a model to include a property to be considered valid",
function() {
User.validatesPresenceOf('first', 'last', 'age');
var joe = new User({first: 'joe'});
assert(joe.isValid() === false, 'model should not validate');
assert(joe.errors.last, 'should have a missing last error');
assert(joe.errors.age, 'should have a missing age error');
});
});
describe('Model.validatesLengthOf(property, options)', function() {
it("Require a property length to be within a specified range",
function() {
User.validatesLengthOf('password',
{min: 5, message: {min: 'Password is too short'}});
var joe = new User({password: '1234'});
assert(joe.isValid() === false, 'model should not be valid');
assert(joe.errors.password, 'should have password error');
});
});
describe('Model.validatesInclusionOf(property, options)', function() {
it("Require a value for `property` to be in the specified array",
function() {
User.validatesInclusionOf('gender', {in: ['male', 'female']});
var foo = new User({gender: 'bar'});
assert(foo.isValid() === false, 'model should not be valid');
assert(foo.errors.gender, 'should have gender error');
});
});
describe('Model.validatesExclusionOf(property, options)', function() {
it("Require a value for `property` to not exist in the specified array",
function() {
User.validatesExclusionOf('domain',
{in: ['www', 'billing', 'admin']});
var foo = new User({domain: 'www'});
var bar = new User({domain: 'billing'});
var bat = new User({domain: 'admin'});
assert(foo.isValid() === false);
assert(bar.isValid() === false);
assert(bat.isValid() === false);
assert(foo.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
});
});
describe('Model.validatesNumericalityOf(property, options)', function() {
it("Require a value for `property` to be a specific type of `Number`",
function() {
User.validatesNumericalityOf('age', {int: true});
var joe = new User({age: 10.2});
assert(joe.isValid() === false);
var bob = new User({age: 0});
assert(bob.isValid() === true);
assert(joe.errors.age, 'model should have an age error');
});
});
describe('myModel.isValid()', function() {
it("Validate the model instance", function() {
User.validatesNumericalityOf('age', {int: true});
var user = new User({first: 'joe', age: 'flarg'})
var valid = user.isValid();
assert(valid === false);
assert(user.errors.age, 'model should have age error');
});
it('Asynchronously validate the model', function(done) {
User.validatesNumericalityOf('age', {int: true});
var user = new User({first: 'joe', age: 'flarg'});
user.isValid(function(valid) {
assert(valid === false);
assert(user.errors.age, 'model should have age error');
done();
});
});
});
describe('Model.create([data], [callback])', function() {
it("Create an instance of Model with given data and save to the attached data source",
function(done) {
User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
assert(user instanceof User);
done();
});
});
});
describe('model.save([options], [callback])', function() {
it("Save an instance of a Model to the attached data source",
function(done) {
var joe = new User({first: 'Joe', last: 'Bob'});
joe.save(function(err, user) {
assert(user.id);
assert(!err);
assert(!user.errors);
done();
});
});
});
describe('model.updateAttributes(data, [callback])', function() {
it("Save specified attributes to the attached data source",
function(done) {
User.create({first: 'joe', age: 100}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
user.updateAttributes({
first: 'updatedFirst',
last: 'updatedLast'
}, function(err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'updatedFirst');
assert.equal(updatedUser.last, 'updatedLast');
assert.equal(updatedUser.age, 100);
done();
});
});
});
});
describe('Model.upsert(data, callback)', function() {
it("Update when record with id=data.id found, insert otherwise",
function(done) {
User.upsert({first: 'joe', id: 7}, function(err, user) {
assert(!err);
assert.equal(user.first, 'joe');
User.upsert({first: 'bob', id: 7}, function(err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'bob');
done();
});
});
});
});
describe('model.destroy([callback])', function() {
it("Remove a model from the attached data source", function(done) {
User.create({first: 'joe', last: 'bob'}, function(err, user) {
User.findById(user.id, function(err, foundUser) {
assert.equal(user.id, foundUser.id);
foundUser.destroy(function() {
User.findById(user.id, function(err, notFound) {
assert.equal(notFound, null);
done();
});
});
});
});
});
});
describe('Model.deleteById(id, [callback])', function() {
it("Delete a model instance from the attached data source",
function(done) {
User.create({first: 'joe', last: 'bob'}, function(err, user) {
User.deleteById(user.id, function(err) {
User.findById(user.id, function(err, notFound) {
assert.equal(notFound, null);
done();
});
});
});
});
});
describe('Model.findById(id, callback)', function() {
it("Find an instance by id", function(done) {
User.create({first: 'michael', last: 'jordan', id: 23}, function() {
User.findById(23, function(err, user) {
assert.equal(user.id, 23);
assert.equal(user.first, 'michael');
assert.equal(user.last, 'jordan');
done();
});
});
});
});
describe('Model.count([query], callback)', function() {
it("Query count of Model instances in data source", function(done) {
(new TaskEmitter()).task(User,
'create',
{first: 'jill', age: 100}).task(User,
'create',
{first: 'bob', age: 200}).task(User,
'create',
{first: 'jan'}).task(User, 'create', {first: 'sam'}).task(User,
'create',
{first: 'suzy'}).on('done', function() {
User.count({age: {gt: 99}}, function(err, count) {
assert.equal(count, 2);
done();
});
});
});
});
});
}