UNPKG

mongoose

Version:

Mongoose MongoDB ODM

1,697 lines (1,409 loc) 125 kB
/** * 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