mongoose
Version:
Mongoose MongoDB ODM
1,697 lines (1,409 loc) • 125 kB
JavaScript
/**
* Test dependencies.
*/
var start = require('./common')
, assert = require('assert')
, mongoose = start.mongoose
, random = require('../lib/utils').random
, Query = require('../lib/query')
, Schema = mongoose.Schema
, SchemaType = mongoose.SchemaType
, CastError = SchemaType.CastError
, ValidatorError = SchemaType.ValidatorError
, ValidationError = mongoose.Document.ValidationError
, ObjectId = Schema.ObjectId
, DocumentObjectId = mongoose.Types.ObjectId
, DocumentArray = mongoose.Types.DocumentArray
, EmbeddedDocument = mongoose.Types.Embedded
, MongooseArray = mongoose.Types.Array
, MongooseError = mongoose.Error;
/**
* Setup.
*/
var Comments = new Schema;
Comments.add({
title : String
, date : Date
, body : String
, comments : [Comments]
});
var BlogPost = new Schema({
title : String
, author : String
, slug : String
, date : Date
, meta : {
date : Date
, visitors : Number
}
, published : Boolean
, mixed : {}
, numbers : [Number]
, owners : [ObjectId]
, comments : [Comments]
, nested : { array: [Number] }
});
BlogPost
.virtual('titleWithAuthor')
.get(function () {
return this.get('title') + ' by ' + this.get('author');
})
.set(function (val) {
var split = val.split(' by ');
this.set('title', split[0]);
this.set('author', split[1]);
});
BlogPost.method('cool', function(){
return this;
});
BlogPost.static('woot', function(){
return this;
});
mongoose.model('BlogPost', BlogPost);
var collection = 'blogposts_' + random();
describe('model', function(){
describe('constructor', function(){
it('works without "new" keyword', function(){
var B = mongoose.model('BlogPost');
var b = B();
assert.ok(b instanceof B);
var db = start();
B = db.model('BlogPost');
db.close();
b = B();
assert.ok(b instanceof B);
})
it('works "new" keyword', function(){
var B = mongoose.model('BlogPost');
var b = new B();
assert.ok(b instanceof B);
var db = start();
B = db.model('BlogPost');
db.close();
b = new B();
assert.ok(b instanceof B);
})
})
describe('isNew', function(){
it('is true on instantiation', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost;
assert.equal(true, post.isNew);
});
it('on parent and subdocs on failed inserts', function(done){
var db = start()
var schema = new Schema({
name: { type: String, unique: true }
, em: [new Schema({ x: Number })]
}, { collection: 'testisnewonfail_'+random() });
var A = db.model('isNewOnFail', schema);
A.on('index', function () {
var a = new A({ name: 'i am new', em: [{ x: 1 }] });
a.save(function (err) {
assert.ifError(err);
assert.equal(a.isNew, false);
assert.equal(a.em[0].isNew, false);
var b = new A({ name: 'i am new', em: [{x:2}] });
b.save(function (err) {
db.close();
assert.ok(err);
assert.equal(b.isNew, true);
assert.equal(b.em[0].isNew, true);
done();
});
});
});
})
});
describe('schema:', function(){
it('should exist', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
assert.ok(BlogPost.schema instanceof Schema);
assert.ok(BlogPost.prototype.schema instanceof Schema);
});
it('emits init event', function(){
var db = start()
, schema = new Schema({ name: String })
, model
schema.on('init', function (model_) {
model = model_;
});
var Named = db.model('EmitInitOnSchema', schema);
db.close();
assert.equal(model,Named);
});
});
describe('structure', function(){
it('default when instantiated', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost;
assert.equal(post.db.model('BlogPost').modelName,'BlogPost');
assert.equal(post.constructor.modelName,'BlogPost');
assert.ok(post.get('_id') instanceof DocumentObjectId);
assert.equal(undefined, post.get('title'));
assert.equal(undefined, post.get('slug'));
assert.equal(undefined, post.get('date'));
assert.equal('object', typeof post.get('meta'));
assert.deepEqual(post.get('meta'), {});
assert.equal(undefined, post.get('meta.date'));
assert.equal(undefined, post.get('meta.visitors'));
assert.equal(undefined, post.get('published'));
assert.equal(1, Object.keys(post.get('nested')).length);
assert.ok(Array.isArray(post.get('nested').array));
assert.ok(post.get('numbers') instanceof MongooseArray);
assert.ok(post.get('owners') instanceof MongooseArray);
assert.ok(post.get('comments') instanceof DocumentArray);
assert.ok(post.get('nested.array') instanceof MongooseArray);
});
describe('array', function(){
describe('defaults', function(){
it('to a non-empty array', function(){
var db = start()
, DefaultArraySchema = new Schema({
arr: {type: Array, cast: String, default: ['a', 'b', 'c']}
, single: {type: Array, cast: String, default: ['a']}
});
mongoose.model('DefaultArray', DefaultArraySchema);
var DefaultArray = db.model('DefaultArray', collection);
var arr = new DefaultArray;
db.close();
assert.equal(arr.get('arr').length, 3)
assert.equal(arr.get('arr')[0],'a');
assert.equal(arr.get('arr')[1],'b');
assert.equal(arr.get('arr')[2],'c');
assert.equal(arr.get('single').length, 1)
assert.equal(arr.get('single')[0],'a');
});
it('empty', function(){
var db = start()
, DefaultZeroCardArraySchema = new Schema({
arr: {type: Array, cast: String, default: []}
, auto: [Number]
});
mongoose.model('DefaultZeroCardArray', DefaultZeroCardArraySchema);
var DefaultZeroCardArray = db.model('DefaultZeroCardArray', collection);
db.close();
var arr = new DefaultZeroCardArray();
assert.equal(arr.get('arr').length, 0);
assert.equal(arr.arr.length, 0);
assert.equal(arr.auto.length, 0);
});
});
})
it('a hash with one null value', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost({
title: null
});
db.close();
assert.strictEqual(null, post.title);
});
it('when saved', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, pending = 2;
function cb () {
if (--pending) return;
db.close();
done();
}
var post = new BlogPost();
post.on('save', function (post) {
assert.ok(post.get('_id') instanceof DocumentObjectId);
assert.equal(undefined, post.get('title'));
assert.equal(undefined, post.get('slug'));
assert.equal(undefined, post.get('date'));
assert.equal(undefined, post.get('published'));
assert.equal(typeof post.get('meta'), 'object');
assert.deepEqual(post.get('meta'), {});
assert.equal(undefined, post.get('meta.date'));
assert.equal(undefined, post.get('meta.visitors'));
assert.ok(post.get('owners') instanceof MongooseArray);
assert.ok(post.get('comments') instanceof DocumentArray);
cb();
});
post.save(function(err, post){
assert.ifError(err);
assert.ok(post.get('_id') instanceof DocumentObjectId);
assert.equal(undefined, post.get('title'));
assert.equal(undefined, post.get('slug'));
assert.equal(undefined, post.get('date'));
assert.equal(undefined, post.get('published'));
assert.equal(typeof post.get('meta'), 'object');
assert.deepEqual(post.get('meta'),{});
assert.equal(undefined, post.get('meta.date'));
assert.equal(undefined, post.get('meta.visitors'));
assert.ok(post.get('owners') instanceof MongooseArray);
assert.ok(post.get('comments') instanceof DocumentArray);
cb();
});
})
describe('init', function(){
it('works', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost()
db.close();
post.init({
title : 'Test'
, slug : 'test'
, date : new Date
, meta : {
date : new Date
, visitors : 5
}
, published : true
, owners : [new DocumentObjectId, new DocumentObjectId]
, comments : [
{ title: 'Test', date: new Date, body: 'Test' }
, { title: 'Super', date: new Date, body: 'Cool' }
]
});
assert.equal(post.get('title'),'Test');
assert.equal(post.get('slug'),'test');
assert.ok(post.get('date') instanceof Date);
assert.equal('object', typeof post.get('meta'));
assert.ok(post.get('meta').date instanceof Date);
assert.equal(typeof post.get('meta').visitors, 'number')
assert.equal(post.get('published'), true);
assert.equal(post.title,'Test');
assert.equal(post.slug,'test');
assert.ok(post.date instanceof Date);
assert.equal(typeof post.meta,'object');
assert.ok(post.meta.date instanceof Date);
assert.equal(typeof post.meta.visitors,'number');
assert.equal(post.published, true);
assert.ok(post.get('owners') instanceof MongooseArray);
assert.ok(post.get('owners')[0] instanceof DocumentObjectId);
assert.ok(post.get('owners')[1] instanceof DocumentObjectId);
assert.ok(post.owners instanceof MongooseArray);
assert.ok(post.owners[0] instanceof DocumentObjectId);
assert.ok(post.owners[1] instanceof DocumentObjectId);
assert.ok(post.get('comments') instanceof DocumentArray);
assert.ok(post.get('comments')[0] instanceof EmbeddedDocument);
assert.ok(post.get('comments')[1] instanceof EmbeddedDocument);
assert.ok(post.comments instanceof DocumentArray);
assert.ok(post.comments[0] instanceof EmbeddedDocument);
assert.ok(post.comments[1] instanceof EmbeddedDocument);
})
it('partially', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost;
post.init({
title : 'Test'
, slug : 'test'
, date : new Date
});
assert.equal(post.get('title'),'Test');
assert.equal(post.get('slug'),'test');
assert.ok(post.get('date') instanceof Date);
assert.equal('object', typeof post.get('meta'));
assert.deepEqual(post.get('meta'),{});
assert.equal(undefined, post.get('meta.date'));
assert.equal(undefined, post.get('meta.visitors'));
assert.equal(undefined, post.get('published'));
assert.ok(post.get('owners') instanceof MongooseArray);
assert.ok(post.get('comments') instanceof DocumentArray);
})
it('with partial hash', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost({
meta: {
date : new Date
, visitors : 5
}
});
assert.equal(5, post.get('meta.visitors').valueOf());
});
it('isNew on embedded documents', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost()
post.init({
title : 'Test'
, slug : 'test'
, comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
});
assert.equal(false, post.get('comments')[0].isNew);
})
it('isNew on embedded documents after saving', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost({ title: 'hocus pocus' })
post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
assert.equal(true, post.get('comments')[0].isNew);
assert.equal(true, post.get('comments')[0].comments[0].isNew);
post.invalidate('title'); // force error
post.save(function (err) {
assert.equal(true, post.isNew);
assert.equal(true, post.get('comments')[0].isNew);
assert.equal(true, post.get('comments')[0].comments[0].isNew);
post.save(function (err) {
db.close();
assert.strictEqual(null, err);
assert.equal(false, post.isNew);
assert.equal(false, post.get('comments')[0].isNew);
assert.equal(false, post.get('comments')[0].comments[0].isNew);
done()
});
});
})
});
});
it('collection name can be specified through schema', function(){
var schema = new Schema({ name: String }, { collection: 'users1' });
var Named = mongoose.model('CollectionNamedInSchema1', schema);
assert.equal(Named.prototype.collection.name,'users1');
var db = start();
var users2schema = new Schema({ name: String }, { collection: 'users2' });
var Named2 = db.model('CollectionNamedInSchema2', users2schema);
db.close();
assert.equal(Named2.prototype.collection.name,'users2');
});
it('saving a model with a null value should perpetuate that null value to the db', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost({
title: null
});
assert.strictEqual(null, post.title);
post.save( function (err) {
assert.strictEqual(err, null);
BlogPost.findById(post.id, function (err, found) {
db.close();
assert.strictEqual(err, null);
assert.strictEqual(found.title, null);
done();
});
});
});
it('instantiating a model with a hash that maps to at least 1 undefined value', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost({
title: undefined
});
assert.strictEqual(undefined, post.title);
post.save( function (err) {
assert.strictEqual(null, err);
BlogPost.findById(post.id, function (err, found) {
db.close();
assert.strictEqual(err, null);
assert.strictEqual(found.title, undefined);
done();
});
});
})
it('modified nested objects which contain MongoseNumbers should not cause a RangeError on save (gh-714)', function(done){
var db =start()
var schema = new Schema({
nested: {
num: Number
}
});
var M = db.model('NestedObjectWithMongooseNumber', schema);
var m = new M;
m.nested = null;
m.save(function (err) {
assert.ifError(err);
M.findById(m, function (err, m) {
assert.ifError(err);
m.nested.num = 5;
m.save(function (err) {
db.close();
assert.ifError(err);
done();
});
});
});
})
it('no RangeError on remove() of a doc with Number _id (gh-714)', function(done){
var db = start()
var MySchema = new Schema({
_id: { type: Number },
name: String
});
var MyModel = db.model('MyModel', MySchema, 'numberrangeerror'+random());
var instance = new MyModel({
name: 'test'
, _id: 35
});
instance.save(function (err) {
assert.ifError(err);
MyModel.findById(35, function (err, doc) {
assert.ifError(err);
doc.remove(function (err) {
db.close();
assert.ifError(err);
done();
});
});
});
});
it('over-writing a number should persist to the db (gh-342)', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost({
meta: {
date : new Date
, visitors : 10
}
});
post.save( function (err) {
assert.ifError(err);
post.set('meta.visitors', 20);
post.save( function (err) {
assert.ifError(err);
BlogPost.findById(post.id, function (err, found) {
assert.ifError(err);
assert.equal(20, found.get('meta.visitors').valueOf());
db.close();
done();
});
});
});
});
describe('methods', function(){
it('can be defined', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
var post = new BlogPost();
assert.equal(post, post.cool());
})
it('can be defined on embedded documents', function(){
var db = start();
var ChildSchema = new Schema({ name: String });
ChildSchema.method('talk', function () {
return 'gaga';
});
var ParentSchema = new Schema({
children: [ChildSchema]
});
var ChildA = db.model('ChildA', ChildSchema, 'children_' + random());
var ParentA = db.model('ParentA', ParentSchema, 'parents_' + random());
db.close();
var c = new ChildA;
assert.equal('function', typeof c.talk);
var p = new ParentA();
p.children.push({});
assert.equal('function', typeof p.children[0].talk);
})
})
describe('statics', function(){
it('can be defined', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection);
db.close();
assert.equal(BlogPost, BlogPost.woot());
});
});
describe('casting', function(){
it('error', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, threw = false;
var post = new BlogPost;
try {
post.init({
date: 'Test'
});
} catch(e){
threw = true;
}
assert.equal(false, threw);
try {
post.set('title', 'Test');
} catch(e){
threw = true;
}
assert.equal(false, threw);
post.save(function(err){
db.close();
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof CastError);
done();
});
})
it('nested error', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, threw = false;
var post = new BlogPost;
try {
post.init({
meta: {
date: 'Test'
}
});
} catch(e){
threw = true;
}
assert.equal(false, threw);
try {
post.set('meta.date', 'Test');
} catch(e){
threw = true;
}
assert.equal(false, threw);
post.save(function(err){
db.close();
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof CastError);
done();
});
});
it('subdocument error', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, threw = false;
var post = new BlogPost()
post.init({
title : 'Test'
, slug : 'test'
, comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
});
post.get('comments')[0].set('date', 'invalid');
post.save(function(err){
db.close();
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof CastError);
done();
});
});
it('subdocument error when adding a subdoc', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, threw = false;
var post = new BlogPost()
try {
post.get('comments').push({
date: 'Bad date'
});
} catch (e) {
threw = true;
}
assert.equal(false, threw);
post.save(function(err){
db.close();
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof CastError);
done();
});
});
it('updates', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost();
post.set('title', '1');
var id = post.get('_id');
post.save(function (err) {
assert.ifError(err);
BlogPost.update({ title: 1, _id: id }, { title: 2 }, function (err) {
assert.ifError(err);
BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
db.close();
assert.ifError(err);
assert.equal(doc.get('title'), '2');
done();
});
});
});
});
it('$pull', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost();
db.close();
post.get('numbers').push('3');
assert.equal(post.get('numbers')[0], 3);
});
it('$push', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost();
post.get('numbers').push(1, 2, 3, 4);
post.save( function (err) {
BlogPost.findById( post.get('_id'), function (err, found) {
assert.equal(found.get('numbers').length,4);
found.get('numbers').pull('3');
found.save( function (err) {
BlogPost.findById(found.get('_id'), function (err, found2) {
db.close();
assert.ifError(err);
assert.equal(found2.get('numbers').length,3);
done();
});
});
});
});
});
it('Number arrays', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var post = new BlogPost();
post.numbers.push(1, '2', 3);
post.save(function (err) {
assert.strictEqual(err, null);
BlogPost.findById(post._id, function (err, doc) {
assert.ifError(err);
assert.ok(~doc.numbers.indexOf(1));
assert.ok(~doc.numbers.indexOf(2));
assert.ok(~doc.numbers.indexOf(3));
db.close();
done();
});
})
});
it('date casting compat with datejs (gh-502)', function(done){
var db = start()
Date.prototype.toObject = function() {
return {
millisecond: 86
, second: 42
, minute: 47
, hour: 17
, day: 13
, week: 50
, month: 11
, year: 2011
};
};
var S = new Schema({
name: String
, description: String
, sabreId: String
, data: {
lastPrice: Number
, comm: String
, curr: String
, rateName: String
}
, created: { type: Date, default: Date.now }
, valid: { type: Boolean, default: true }
});
var M = db.model('gh502', S);
var m = new M;
m.save(function (err) {
assert.ifError(err);
M.findById(m._id, function (err, m) {
assert.ifError(err);
m.save(function (err) {
assert.ifError(err);
M.remove(function (err) {
db.close();
delete Date.prototype.toObject;
assert.ifError(err);
done();
});
});
});
});
});
});
describe('validation', function(){
it('works', function(done){
function dovalidate (val) {
assert.equal('correct', this.asyncScope);
return true;
}
function dovalidateAsync (val, callback) {
assert.equal('correct', this.scope);
process.nextTick(function () {
callback(true);
});
}
mongoose.model('TestValidation', new Schema({
simple: { type: String, required: true }
, scope: { type: String, validate: [dovalidate, 'scope failed'], required: true }
, asyncScope: { type: String, validate: [dovalidateAsync, 'async scope failed'], required: true }
}));
var db = start()
, TestValidation = db.model('TestValidation');
var post = new TestValidation();
post.set('simple', '');
post.set('scope', 'correct');
post.set('asyncScope', 'correct');
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
post.set('simple', 'here');
post.save(function(err){
db.close();
assert.ifError(err);
done();
});
});
});
it('custom messaging', function(done){
function validate (val) {
return val === 'abc';
}
mongoose.model('TestValidationMessage', new Schema({
simple: { type: String, validate: [validate, 'must be abc'] }
}));
var db = start()
, TestValidationMessage = db.model('TestValidationMessage');
var post = new TestValidationMessage();
post.set('simple', '');
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(err.errors.simple instanceof ValidatorError);
assert.equal(err.errors.simple.message,'Validator "must be abc" failed for path simple');
assert.equal(post.errors.simple.message,'Validator "must be abc" failed for path simple');
post.set('simple', 'abc');
post.save(function(err){
db.close();
assert.ifError(err);
done();
});
});
})
it('with Model.schema.path introspection (gh-272)', function(done){
var db = start();
var IntrospectionValidationSchema = new Schema({
name: String
});
var IntrospectionValidation = db.model('IntrospectionValidation', IntrospectionValidationSchema, 'introspections_' + random());
IntrospectionValidation.schema.path('name').validate(function (value) {
return value.length < 2;
}, 'Name cannot be greater than 1 character');
var doc = new IntrospectionValidation({name: 'hi'});
doc.save( function (err) {
db.close();
assert.equal(err.errors.name.message,"Validator \"Name cannot be greater than 1 character\" failed for path name");
assert.equal(err.name,"ValidationError");
assert.equal(err.message,"Validation failed");
done();
});
});
it('of required undefined values', function(done){
mongoose.model('TestUndefinedValidation', new Schema({
simple: { type: String, required: true }
}));
var db = start()
, TestUndefinedValidation = db.model('TestUndefinedValidation');
var post = new TestUndefinedValidation;
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
post.set('simple', 'here');
post.save(function(err){
db.close();
assert.ifError(err);
done();
});
});
})
it('save callback should only execute once (gh-319)', function(done){
var db = start()
var D = db.model('CallbackFiresOnceValidation', new Schema({
username: { type: String, validate: /^[a-z]{6}$/i }
, email: { type: String, validate: /^[a-z]{6}$/i }
, password: { type: String, validate: /^[a-z]{6}$/i }
}));
var post = new D({
username: "nope"
, email: "too"
, password: "short"
});
var timesCalled = 0;
post.save(function (err) {
db.close();
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.equal(1, ++timesCalled);
assert.equal(Object.keys(err.errors).length, 3);
assert.ok(err.errors.password instanceof ValidatorError);
assert.ok(err.errors.email instanceof ValidatorError);
assert.ok(err.errors.username instanceof ValidatorError);
assert.equal(err.errors.password.message,'Validator failed for path password');
assert.equal(err.errors.email.message,'Validator failed for path email');
assert.equal(err.errors.username.message,'Validator failed for path username');
assert.equal(Object.keys(post.errors).length, 3);
assert.ok(post.errors.password instanceof ValidatorError);
assert.ok(post.errors.email instanceof ValidatorError);
assert.ok(post.errors.username instanceof ValidatorError);
assert.equal(post.errors.password.message,'Validator failed for path password');
assert.equal(post.errors.email.message,'Validator failed for path email');
assert.equal(post.errors.username.message,'Validator failed for path username');
done();
});
});
it('query result', function(done){
mongoose.model('TestValidationOnResult', new Schema({
resultv: { type: String, required: true }
}));
var db = start()
, TestV = db.model('TestValidationOnResult');
var post = new TestV;
post.validate(function (err) {
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
post.resultv = 'yeah';
post.save(function (err) {
assert.ifError(err);
TestV.findOne({ _id: post.id }, function (err, found) {
assert.ifError(err);
assert.equal(found.resultv,'yeah');
found.save(function(err){
db.close();
assert.ifError(err);
done();
})
});
});
});
});
it('of required previously existing null values', function(done){
mongoose.model('TestPreviousNullValidation', new Schema({
previous: { type: String, required: true }
, a: String
}));
var db = start()
, TestP = db.model('TestPreviousNullValidation')
TestP.collection.insert({ a: null, previous: null}, {}, function (err, f) {
assert.ifError(err);
TestP.findOne({_id: f[0]._id}, function (err, found) {
assert.ifError(err);
assert.equal(false, found.isNew);
assert.strictEqual(found.get('previous'), null);
found.validate(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
found.set('previous', 'yoyo');
found.save(function (err) {
assert.strictEqual(err, null);
db.close();
done()
});
})
})
});
})
it('nested', function(done){
mongoose.model('TestNestedValidation', new Schema({
nested: {
required: { type: String, required: true }
}
}));
var db = start()
, TestNestedValidation = db.model('TestNestedValidation');
var post = new TestNestedValidation();
post.set('nested.required', null);
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
post.set('nested.required', 'here');
post.save(function(err){
db.close();
assert.ifError(err);
done();
});
});
})
it('of nested subdocuments', function(done){
var Subsubdocs= new Schema({ required: { type: String, required: true }});
var Subdocs = new Schema({
required: { type: String, required: true }
, subs: [Subsubdocs]
});
mongoose.model('TestSubdocumentsValidation', new Schema({
items: [Subdocs]
}));
var db = start()
, TestSubdocumentsValidation = db.model('TestSubdocumentsValidation');
var post = new TestSubdocumentsValidation();
post.get('items').push({ required: '', subs: [{required: ''}] });
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(err.errors['items.0.subs.0.required'] instanceof ValidatorError);
assert.equal(err.errors['items.0.subs.0.required'].message,'Validator "required" failed for path required');
assert.ok(post.errors['items.0.subs.0.required'] instanceof ValidatorError);
assert.equal(post.errors['items.0.subs.0.required'].message,'Validator "required" failed for path required');
assert.ok(!err.errors['items.0.required']);
assert.ok(!err.errors['items.0.required']);
assert.ok(!post.errors['items.0.required']);
assert.ok(!post.errors['items.0.required']);
post.items[0].subs[0].set('required', true);
assert.equal(undefined, post._validationError);
post.save(function(err){
assert.ok(err);
assert.ok(err.errors);
assert.ok(err.errors['items.0.required'] instanceof ValidatorError);
assert.equal(err.errors['items.0.required'].message,'Validator "required" failed for path required');
assert.ok(!err.errors['items.0.subs.0.required']);
assert.ok(!err.errors['items.0.subs.0.required']);
assert.ok(!post.errors['items.0.subs.0.required']);
assert.ok(!post.errors['items.0.subs.0.required']);
post.get('items')[0].set('required', true);
post.save(function(err){
db.close();
assert.ok(!post.errors);
assert.ifError(err);
done();
});
});
});
});
describe('async', function(){
it('works', function(done){
var executed = false;
function validator(v, fn){
setTimeout(function () {
executed = true;
fn(v !== 'test');
}, 5);
};
mongoose.model('TestAsyncValidation', new Schema({
async: { type: String, validate: [validator, 'async validator'] }
}));
var db = start()
, TestAsyncValidation = db.model('TestAsyncValidation');
var post = new TestAsyncValidation();
post.set('async', 'test');
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(err.errors.async instanceof ValidatorError);
assert.equal(err.errors.async.message,'Validator "async validator" failed for path async');
assert.equal(true, executed);
executed = false;
post.set('async', 'woot');
post.save(function(err){
db.close();
assert.equal(true, executed);
assert.strictEqual(err, null);
done();
});
});
})
it('nested', function(done){
var executed = false;
function validator(v, fn){
setTimeout(function () {
executed = true;
fn(v !== 'test');
}, 5);
};
mongoose.model('TestNestedAsyncValidation', new Schema({
nested: {
async: { type: String, validate: [validator, 'async validator'] }
}
}));
var db = start()
, TestNestedAsyncValidation = db.model('TestNestedAsyncValidation');
var post = new TestNestedAsyncValidation();
post.set('nested.async', 'test');
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(executed);
executed = false;
post.validate(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(executed);
executed = false;
post.set('nested.async', 'woot');
post.validate(function(err){
assert.ok(executed);
assert.equal(err, null);
executed = false;
post.save(function(err){
db.close();
assert.ok(executed);
assert.strictEqual(err, null);
done();
});
});
});
});
});
it('subdocuments', function(done){
var executed = false;
function validator (v, fn) {
setTimeout(function(){
executed = true;
fn(v !== '');
}, 5);
};
var Subdocs = new Schema({
required: { type: String, validate: [validator, 'async in subdocs'] }
});
mongoose.model('TestSubdocumentsAsyncValidation', new Schema({
items: [Subdocs]
}));
var db = start()
, Test = db.model('TestSubdocumentsAsyncValidation');
var post = new Test();
post.get('items').push({ required: '' });
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.ok(executed);
executed = false;
post.get('items')[0].set({ required: 'here' });
post.save(function(err){
db.close();
assert.ok(executed);
assert.strictEqual(err, null);
done();
});
});
});
});
it('without saving', function(done){
mongoose.model('TestCallingValidation', new Schema({
item: { type: String, required: true }
}));
var db = start()
, TestCallingValidation = db.model('TestCallingValidation');
var post = new TestCallingValidation;
assert.equal(true, post.schema.path('item').isRequired);
assert.strictEqual(post.isNew, true);
post.validate(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.strictEqual(post.isNew, true);
post.item = 'yo';
post.validate(function(err){
db.close();
assert.equal(err, null);
assert.strictEqual(post.isNew, true);
done();
});
});
});
it('when required is set to false', function(){
function validator () {
return true;
}
mongoose.model('TestRequiredFalse', new Schema({
result: { type: String, validate: [validator, 'chump validator'], required: false }
}));
var db = start()
, TestV = db.model('TestRequiredFalse');
var post = new TestV;
db.close();
assert.equal(false, post.schema.path('result').isRequired);
})
describe('middleware', function(){
it('works', function(done){
var db = start()
, ValidationMiddlewareSchema = null
, Post = null
, post = null;
ValidationMiddlewareSchema = new Schema({
baz: { type: String }
});
ValidationMiddlewareSchema.pre('validate', function(next) {
if (this.get('baz') == 'bad') {
this.invalidate('baz', 'bad');
}
next();
});
mongoose.model('ValidationMiddleware', ValidationMiddlewareSchema);
Post = db.model('ValidationMiddleware');
post = new Post();
post.set({baz: 'bad'});
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.equal(err.errors.baz.type,'bad');
assert.equal(err.errors.baz.path,'baz');
post.set('baz', 'good');
post.save(function(err){
db.close();
assert.strictEqual(err, null);
done();
});
});
})
it('async', function(done){
var db = start()
, AsyncValidationMiddlewareSchema = null
, Post = null
, post = null;
AsyncValidationMiddlewareSchema = new Schema({
prop: { type: String }
});
AsyncValidationMiddlewareSchema.pre('validate', true, function(next, done) {
var self = this;
setTimeout(function() {
if (self.get('prop') == 'bad') {
self.invalidate('prop', 'bad');
}
done();
}, 5);
next();
});
mongoose.model('AsyncValidationMiddleware', AsyncValidationMiddlewareSchema);
Post = db.model('AsyncValidationMiddleware');
post = new Post();
post.set({prop: 'bad'});
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.equal(err.errors.prop.type,'bad');
assert.equal(err.errors.prop.path,'prop');
post.set('prop', 'good');
post.save(function(err){
db.close();
assert.strictEqual(err, null);
done();
});
});
});
it('complex', function(done){
var db = start()
, ComplexValidationMiddlewareSchema = null
, Post = null
, post = null
, abc = function(v) {
return v === 'abc';
};
ComplexValidationMiddlewareSchema = new Schema({
baz: { type: String },
abc: { type: String, validate: [abc, 'must be abc'] },
test: { type: String, validate: [/test/, 'must also be abc'] },
required: { type: String, required: true }
});
ComplexValidationMiddlewareSchema.pre('validate', true, function(next, done) {
var self = this;
setTimeout(function() {
if (self.get('baz') == 'bad') {
self.invalidate('baz', 'bad');
}
done();
}, 5);
next();
});
mongoose.model('ComplexValidationMiddleware', ComplexValidationMiddlewareSchema);
Post = db.model('ComplexValidationMiddleware');
post = new Post();
post.set({
baz: 'bad',
abc: 'not abc',
test: 'fail'
});
post.save(function(err){
assert.ok(err instanceof MongooseError);
assert.ok(err instanceof ValidationError);
assert.equal(4, Object.keys(err.errors).length);
assert.ok(err.errors.baz instanceof ValidatorError);
assert.equal(err.errors.baz.type,'bad');
assert.equal(err.errors.baz.path,'baz');
assert.ok(err.errors.abc instanceof ValidatorError);
assert.equal(err.errors.abc.type,'must be abc');
assert.equal(err.errors.abc.path,'abc');
assert.ok(err.errors.test instanceof ValidatorError);
assert.equal(err.errors.test.type,'must also be abc');
assert.equal(err.errors.test.path,'test');
assert.ok(err.errors.required instanceof ValidatorError);
assert.equal(err.errors.required.type,'required');
assert.equal(err.errors.required.path,'required');
post.set({
baz: 'good',
abc: 'abc',
test: 'test',
required: 'here'
});
post.save(function(err){
db.close();
assert.strictEqual(err, null);
done();
});
});
})
})
});
describe('defaults application', function(){
it('works', function(){
var now = Date.now();
mongoose.model('TestDefaults', new Schema({
date: { type: Date, default: now }
}));
var db = start()
, TestDefaults = db.model('TestDefaults');
db.close();
var post = new TestDefaults;
assert.ok(post.get('date') instanceof Date);
assert.equal(+post.get('date'), now);
});
it('nested', function(){
var now = Date.now();
mongoose.model('TestNestedDefaults', new Schema({
nested: {
date: { type: Date, default: now }
}
}));
var db = start()
, TestDefaults = db.model('TestNestedDefaults');
var post = new TestDefaults();
db.close();
assert.ok(post.get('nested.date') instanceof Date);
assert.equal(+post.get('nested.date'), now);
})
it('subdocument', function(){
var now = Date.now();
var Items = new Schema({
date: { type: Date, default: now }
});
mongoose.model('TestSubdocumentsDefaults', new Schema({
items: [Items]
}));
var db = start()
, TestSubdocumentsDefaults = db.model('TestSubdocumentsDefaults');
db.close();
var post = new TestSubdocumentsDefaults();
post.get('items').push({});
assert.ok(post.get('items')[0].get('date') instanceof Date);
assert.equal(+post.get('items')[0].get('date'), now);
});
it('allows nulls', function(done){
var db = start();
var T = db.model('NullDefault', new Schema({ name: { type: String, default: null }}), collection);
var t = new T();
assert.strictEqual(null, t.name);
t.save(function (err) {
assert.ifError(err);
T.findById(t._id, function (err, t) {
db.close();
assert.ifError(err);
assert.strictEqual(null, t.name);
done();
});
});
});
});
describe('virtuals', function(){
it('getters', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost({
title: 'Letters from Earth'
, author: 'Mark Twain'
});
db.close();
assert.equal(post.get('titleWithAuthor'), 'Letters from Earth by Mark Twain');
assert.equal(post.titleWithAuthor,'Letters from Earth by Mark Twain');
});
it('set()', function(){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost();
db.close();
post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain')
assert.equal(post.get('title'),'Huckleberry Finn');
assert.equal(post.get('author'),'Mark Twain');
});
it('should not be saved to the db', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost();
post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain');
post.save(function (err) {
assert.ifError(err);
BlogPost.findById(post.get('_id'), function (err, found) {
assert.ifError(err);
assert.equal(found.get('title'),'Huckleberry Finn');
assert.equal(found.get('author'),'Mark Twain');
assert.ok(! ('titleWithAuthor' in found.toObject()));
db.close();
done();
});
});
});
it('nested', function(){
var db = start()
, PersonSchema = new Schema({
name: {
first: String
, last: String
}
});
PersonSchema
.virtual('name.full')
.get(function () {
return this.get('name.first') + ' ' + this.get('name.last');
})
.set(function (fullName) {
var split = fullName.split(' ');
this.set('name.first', split[0]);
this.set('name.last', split[1]);
});
mongoose.model('Person', PersonSchema);
var Person = db.model('Person')
, person = new Person({
name: {
first: 'Michael'
, last: 'Sorrentino'
}
});
db.close();
assert.equal(person.get('name.full'),'Michael Sorrentino');
person.set('name.full', 'The Situation');
assert.equal(person.get('name.first'),'The');
assert.equal(person.get('name.last'),'Situation');
assert.equal(person.name.full,'The Situation');
person.name.full = 'Michael Sorrentino';
assert.equal(person.name.first,'Michael');
assert.equal(person.name.last,'Sorrentino');
});
});
describe('remove()', function(){
it('works', function(done){
var db = start()
, collection = 'blogposts_' + random()
, BlogPost = db.model('BlogPost', collection)
, post = new BlogPost();
post.save(function (err) {
assert.ifError(err);
BlogPost.find({}, function (err, found) {
assert.ifError(err);
assert.equal(1, found.length);
BlogPost.remove({}, function (err) {
assert.ifError(err);
BlogPost.find({}, function (err, found2) {
db.close();
assert.ifError(err);
assert.equal(0, found2.length);
done();
});
});
});
});
});
});
describe('getters', function(){
it('with same name on embedded docs do not class', function(){
var Post = new Schema({
title : String
, author : { name : String }
, subject : { name : String }
});
mongoose.model('PostWithClashGetters', Post);
var db = start()
, PostModel = db.model('PostWithClashGetters', 'postwithclash' + random());
var post = new PostModel({
title: 'Test'
, author: { name: 'A' }
, subject: { name: 'B' }
});
db.close();
assert.equal(p