mongoose
Version:
Mongoose MongoDB ODM
1,285 lines (1,052 loc) • 37 kB
JavaScript
/**
* Module dependencies.
*/
var start = require('./common')
, mongoose = start.mongoose
, assert = require('assert')
, Schema = mongoose.Schema
, Document = mongoose.Document
, SchemaType = mongoose.SchemaType
, VirtualType = mongoose.VirtualType
, ObjectId = Schema.ObjectId
, ValidatorError = SchemaType.ValidatorError
, CastError = SchemaType.CastError
, SchemaTypes = Schema.Types
, DocumentObjectId = mongoose.Types.ObjectId
, Mixed = SchemaTypes.Mixed
, MongooseArray = mongoose.Types.Array
, vm = require('vm')
/**
* Test Document constructor.
*/
function TestDocument () {
Document.apply(this, arguments);
};
/**
* Inherits from Document.
*/
TestDocument.prototype.__proto__ = Document.prototype;
/**
* Set a dummy schema to simulate compilation.
*/
TestDocument.prototype._setSchema(new Schema({
test : String
}));
/**
* Test.
*/
describe('schema', function(){
it('can be created without the "new" keyword', function(){
var schema = Schema({ name: String });
assert.ok(schema instanceof Schema);
})
it('supports different schematypes', function(){
var Checkin = new Schema({
date : Date
, location : {
lat: Number
, lng: Number
}
});
var Ferret = new Schema({
name : String
, owner : ObjectId
, fur : String
, color : { type: String }
, age : Number
, checkins : [Checkin]
, friends : [ObjectId]
, likes : Array
, alive : Boolean
, extra : Mixed
});
assert.ok(Ferret.path('name') instanceof SchemaTypes.String);
assert.ok(Ferret.path('owner') instanceof SchemaTypes.ObjectId);
assert.ok(Ferret.path('fur') instanceof SchemaTypes.String);
assert.ok(Ferret.path('color') instanceof SchemaTypes.String);
assert.ok(Ferret.path('age') instanceof SchemaTypes.Number);
assert.ok(Ferret.path('checkins') instanceof SchemaTypes.DocumentArray);
assert.ok(Ferret.path('friends') instanceof SchemaTypes.Array);
assert.ok(Ferret.path('likes') instanceof SchemaTypes.Array);
assert.ok(Ferret.path('alive') instanceof SchemaTypes.Boolean);
assert.ok(Ferret.path('extra') instanceof SchemaTypes.Mixed);
assert.strictEqual(Ferret.path('unexistent'), undefined);
assert.ok(Checkin.path('date') instanceof SchemaTypes.Date);
// check strings
var Checkin1 = new Schema({
date : 'date'
, location : {
lat: 'number'
, lng: 'Number'
}
});
assert.ok(Checkin1.path('date') instanceof SchemaTypes.Date);
assert.ok(Checkin1.path('location.lat') instanceof SchemaTypes.Number);
assert.ok(Checkin1.path('location.lng') instanceof SchemaTypes.Number);
var Ferret1 = new Schema({
name : "string"
, owner : "oid"
, fur : { type: "string" }
, color : { type: "String" }
, checkins : [Checkin]
, friends : Array
, likes : "array"
, alive : "Bool"
, alive1 : "bool"
, alive2 : "boolean"
, extra : "mixed"
, obj : "object"
, buf : "buffer"
, Buf : "Buffer"
});
assert.ok(Ferret1.path('name') instanceof SchemaTypes.String);
assert.ok(Ferret1.path('owner') instanceof SchemaTypes.ObjectId);
assert.ok(Ferret1.path('fur') instanceof SchemaTypes.String);
assert.ok(Ferret1.path('color') instanceof SchemaTypes.String);
assert.ok(Ferret1.path('checkins') instanceof SchemaTypes.DocumentArray);
assert.ok( Ferret1.path('friends') instanceof SchemaTypes.Array);
assert.ok(Ferret1.path('likes') instanceof SchemaTypes.Array);
assert.ok(Ferret1.path('alive') instanceof SchemaTypes.Boolean);
assert.ok(Ferret1.path('alive1') instanceof SchemaTypes.Boolean);
assert.ok(Ferret1.path('alive2') instanceof SchemaTypes.Boolean);
assert.ok(Ferret1.path('extra') instanceof SchemaTypes.Mixed);
assert.ok(Ferret1.path('obj') instanceof SchemaTypes.Mixed);
assert.ok(Ferret1.path('buf') instanceof SchemaTypes.Buffer);
assert.ok(Ferret1.path('Buf') instanceof SchemaTypes.Buffer);
});
it('supports dot notation for path accessors', function(){
var Racoon = new Schema({
name : { type: String, enum: ['Edwald', 'Tobi'] }
, age : Number
});
var Person = new Schema({
name : String
, raccoons : [Racoon]
, location : {
city : String
, state : String
}
});
assert.ok(Person.path('name') instanceof SchemaTypes.String);
assert.ok(Person.path('raccoons') instanceof SchemaTypes.DocumentArray);
assert.ok(Person.path('location.city') instanceof SchemaTypes.String);
assert.ok(Person.path('location.state') instanceof SchemaTypes.String);
assert.strictEqual(Person.path('location.unexistent'), undefined);
})
it('allows paths nested > 2 levels', function(){
var Nested = new Schema({
first: {
second: {
third: String
}
}
});
assert.ok(Nested.path('first.second.third') instanceof SchemaTypes.String);
});
it('default definition', function(){
var Test = new Schema({
simple : { type: String, default: 'a' }
, array : { type: Array, default: [1,2,3,4,5] }
, arrayX : { type: Array, default: 9 }
, arrayFn : { type: Array, default: function () { return [8] } }
, callback : { type: Number, default: function(){
assert.equal('b', this.a);
return '3';
}}
});
assert.equal(Test.path('simple').defaultValue, 'a');
assert.equal(typeof Test.path('callback').defaultValue, 'function');
assert.equal(Test.path('simple').getDefault(), 'a');
assert.equal((+Test.path('callback').getDefault({ a: 'b' })), 3);
assert.equal(typeof Test.path('array').defaultValue, 'function');
assert.equal(Test.path('array').getDefault(new TestDocument)[3], 4);
assert.equal(Test.path('arrayX').getDefault(new TestDocument)[0], 9);
assert.equal(typeof Test.path('arrayFn').defaultValue, 'function');
assert.ok(Test.path('arrayFn').getDefault(new TestDocument) instanceof MongooseArray);
})
it('Mixed defaults can be empty arrays', function(){
var Test = new Schema({
mixed1 : { type: Mixed, default: [] }
, mixed2 : { type: Mixed, default: Array }
});
assert.ok(Test.path('mixed1').getDefault() instanceof Array);
assert.equal(Test.path('mixed1').getDefault().length, 0);
assert.ok(Test.path('mixed2').getDefault() instanceof Array);
assert.equal(Test.path('mixed2').getDefault().length, 0);
})
describe('validation', function(){
it('invalid arguments are rejected (1044)', function(){
assert.throws(function () {
new Schema({
simple: { type: String, validate: 'nope' }
});
}, /Invalid validator/);
assert.throws(function () {
new Schema({
simple: { type: String, validate: ['nope'] }
});
}, /Invalid validator/);
assert.throws(function () {
new Schema({
simple: { type: String, validate: { nope: 1, msg: 'nope' } }
});
}, /Invalid validator/);
assert.throws(function () {
new Schema({
simple: { type: String, validate: [{ nope: 1, msg: 'nope' }, 'nope'] }
});
}, /Invalid validator/);
})
it('string required', function(){
var Test = new Schema({
simple: String
});
Test.path('simple').required(true);
assert.equal(Test.path('simple').validators.length, 1);
Test.path('simple').doValidate(null, function(err){
assert.ok(err instanceof ValidatorError);
});
Test.path('simple').doValidate(undefined, function(err){
assert.ok(err instanceof ValidatorError);
});
Test.path('simple').doValidate('', function(err){
assert.ok(err instanceof ValidatorError);
});
Test.path('simple').doValidate('woot', function(err){
assert.ifError(err);
});
});
it('string enum', function(){
var Test = new Schema({
complex: { type: String, enum: ['a', 'b', undefined, 'c', null] }
});
assert.ok(Test.path('complex') instanceof SchemaTypes.String);
assert.deepEqual(Test.path('complex').enumValues,['a', 'b', 'c', null]);
assert.equal(Test.path('complex').validators.length, 1)
Test.path('complex').enum('d', 'e');
assert.deepEqual(Test.path('complex').enumValues, ['a', 'b', 'c', null, 'd', 'e']);
Test.path('complex').doValidate('x', function(err){
assert.ok(err instanceof ValidatorError);
});
// allow unsetting enums
Test.path('complex').doValidate(undefined, function(err){
assert.ifError(err);
});
Test.path('complex').doValidate(null, function(err){
assert.ifError(err);
});
Test.path('complex').doValidate('da', function(err){
assert.ok(err instanceof ValidatorError);
})
})
it('string regexp', function(){
var Test = new Schema({
simple: { type: String, match: /[a-z]/ }
});
assert.equal(1, Test.path('simple').validators.length);
Test.path('simple').doValidate('az', function(err){
assert.ifError(err);
});
Test.path('simple').match(/[0-9]/);
assert.equal(2, Test.path('simple').validators.length);
Test.path('simple').doValidate('12', function(err){
assert.ok(err instanceof ValidatorError);
});
Test.path('simple').doValidate('a12', function(err){
assert.ifError(err);
});
Test.path('simple').doValidate('', function(err){
assert.ifError(err);
});
Test.path('simple').doValidate(null, function(err){
assert.ifError(err);
});
Test.path('simple').doValidate(undefined, function(err){
assert.ifError(err);
});
Test.path('simple').validators = [];
Test.path('simple').match(/[1-9]/);
Test.path('simple').doValidate(0, function(err){
assert.ok(err instanceof ValidatorError);
});
})
it('number min and max', function(){
var Tobi = new Schema({
friends: { type: Number, max: 15, min: 5 }
});
assert.equal(Tobi.path('friends').validators.length, 2);
Tobi.path('friends').doValidate(10, function(err){
assert.ifError(err);
});
Tobi.path('friends').doValidate(100, function(err){
assert.ok(err instanceof ValidatorError);
});
Tobi.path('friends').doValidate(1, function(err){
assert.ok(err instanceof ValidatorError);
});
// null is allowed
Tobi.path('friends').doValidate(null, function(err){
assert.ifError(err);
});
});
it('number required', function(){
var Edwald = new Schema({
friends: { type: Number, required: true }
});
Edwald.path('friends').doValidate(null, function(err){
assert.ok(err instanceof ValidatorError);
});
Edwald.path('friends').doValidate(undefined, function(err){
assert.ok(err instanceof ValidatorError);
});
Edwald.path('friends').doValidate(0, function(err){
assert.ifError(err);
});
})
it('date required', function(){
var Loki = new Schema({
birth_date: { type: Date, required: true }
});
Loki.path('birth_date').doValidate(null, function (err) {
assert.ok(err instanceof ValidatorError);
});
Loki.path('birth_date').doValidate(undefined, function (err) {
assert.ok(err instanceof ValidatorError);
});
Loki.path('birth_date').doValidate(new Date(), function (err) {
assert.ifError(err);
});
});
it('objectid required', function(){
var Loki = new Schema({
owner: { type: ObjectId, required: true }
});
Loki.path('owner').doValidate(new DocumentObjectId(), function(err){
assert.ifError(err);
});
Loki.path('owner').doValidate(null, function(err){
assert.ok(err instanceof ValidatorError);
});
Loki.path('owner').doValidate(undefined, function(err){
assert.ok(err instanceof ValidatorError);
});
});
it('array required', function(){
var Loki = new Schema({
likes: { type: Array, required: true }
});
Loki.path('likes').doValidate(null, function (err) {
assert.ok(err instanceof ValidatorError);
});
Loki.path('likes').doValidate(undefined, function (err) {
assert.ok(err instanceof ValidatorError);
});
Loki.path('likes').doValidate([], function (err) {
assert.ok(err instanceof ValidatorError);
});
});
it('boolean required', function(){
var Animal = new Schema({
isFerret: { type: Boolean, required: true }
});
Animal.path('isFerret').doValidate(null, function(err){
assert.ok(err instanceof ValidatorError);
});
Animal.path('isFerret').doValidate(undefined, function(err){
assert.ok(err instanceof ValidatorError);
});
Animal.path('isFerret').doValidate(true, function(err){
assert.ifError(err);
});
Animal.path('isFerret').doValidate(false, function(err){
assert.ifError(err);
});
});
describe('async', function(){
it('works', function(done){
var executed = 0;
function validator (value, fn) {
setTimeout(function(){
executed++;
fn(value === true);
if (2 === executed) done();
}, 5);
};
var Animal = new Schema({
ferret: { type: Boolean, validate: validator }
});
Animal.path('ferret').doValidate(true, function(err){
assert.ifError(err);
});
Animal.path('ferret').doValidate(false, function(err){
assert.ok(err instanceof Error);
});
});
it('multiple', function(done) {
var executed = 0;
function validator (value, fn) {
setTimeout(function(){
executed++;
fn(value === true);
if (2 === executed) done();
}, 5);
};
var Animal = new Schema({
ferret: {
type: Boolean,
validate: [
{
'validator': validator,
'msg': 'validator1'
},
{
'validator': validator,
'msg': 'validator2'
},
],
}
});
Animal.path('ferret').doValidate(true, function(err){
assert.ifError(err);
});
});
it('scope', function(done){
var called = false;
function validator (value, fn) {
assert.equal('b', this.a);
setTimeout(function(){
called = true;
fn(true);
}, 5);
};
var Animal = new Schema({
ferret: { type: Boolean, validate: validator }
});
Animal.path('ferret').doValidate(true, function(err){
assert.ifError(err);
assert.equal(true, called);
done();
}, { a: 'b' });
})
})
});
describe('casting', function(){
it('number', function(){
var Tobi = new Schema({
age: Number
});
// test String -> Number cast
assert.equal('number', typeof Tobi.path('age').cast('0'));
assert.equal(0, (+Tobi.path('age').cast('0')));
assert.equal('number', typeof Tobi.path('age').cast(0));
assert.equal(0, (+Tobi.path('age').cast(0)));
});
describe('string', function(){
it('works', function(){
var Tobi = new Schema({
nickname: String
});
function Test(){};
Test.prototype.toString = function(){
return 'woot';
};
// test Number -> String cast
assert.equal('string', typeof Tobi.path('nickname').cast(0));
assert.equal('0', Tobi.path('nickname').cast(0));
// test any object that implements toString
assert.equal('string', typeof Tobi.path('nickname').cast(new Test));
assert.equal('woot', Tobi.path('nickname').cast(new Test));
});
it('casts undefined to "undefined"', function(done){
var db= require('./common')();
var schema = new Schema({ arr: [String] });
var M = db.model('castingStringArrayWithUndefined', schema);
M.find({ arr: { $in: [undefined] }}, function (err) {
db.close();
assert.equal(err && err.message, 'Cast to string failed for value "undefined"');
done();
});
});
});
it('date', function(){
var Loki = new Schema({
birth_date: { type: Date }
});
assert.ok(Loki.path('birth_date').cast(1294525628301) instanceof Date);
assert.ok(Loki.path('birth_date').cast('8/24/2000') instanceof Date);
assert.ok(Loki.path('birth_date').cast(new Date) instanceof Date);
});
it('objectid', function(){
var Loki = new Schema({
owner: { type: ObjectId }
});
var doc = new TestDocument()
, id = doc._id.toString();
assert.ok(Loki.path('owner').cast('4c54f3453e688c000000001a')
instanceof DocumentObjectId);
assert.ok(Loki.path('owner').cast(new DocumentObjectId())
instanceof DocumentObjectId);
assert.ok(Loki.path('owner').cast(doc)
instanceof DocumentObjectId);
assert.equal(id, Loki.path('owner').cast(doc).toString());
});
it('array', function(){
var Loki = new Schema({
oids : [ObjectId]
, dates : [Date]
, numbers : [Number]
, strings : [String]
, buffers : [Buffer]
, nocast : []
, mixed : [Mixed]
});
var oids = Loki.path('oids').cast(['4c54f3453e688c000000001a', new DocumentObjectId]);
assert.ok(oids[0] instanceof DocumentObjectId);
assert.ok(oids[1] instanceof DocumentObjectId);
var dates = Loki.path('dates').cast(['8/24/2010', 1294541504958]);
assert.ok(dates[0] instanceof Date);
assert.ok(dates[1] instanceof Date);
var numbers = Loki.path('numbers').cast([152, '31']);
assert.equal(typeof numbers[0], 'number');
assert.equal(typeof numbers[1], 'number');
var strings = Loki.path('strings').cast(['test', 123]);
assert.equal(typeof strings[0], 'string');
assert.equal('test',strings[0]);
assert.equal(typeof strings[1], 'string');
assert.equal('123', strings[1]);
var buffers = Loki.path('buffers').cast(['\0\0\0', new Buffer("abc")]);
assert.ok(buffers[0] instanceof Buffer);
assert.ok(buffers[1] instanceof Buffer);
var nocasts = Loki.path('nocast').cast(['test', 123]);
assert.equal(typeof nocasts[0], 'string');
assert.equal('test', nocasts[0]);
assert.equal(typeof nocasts[1], 'number');
assert.equal(123, nocasts[1]);
var mixed = Loki.path('mixed').cast(['test', 123, '123', {}, new Date, new DocumentObjectId]);
assert.equal(typeof mixed[0], 'string');
assert.equal(typeof mixed[1], 'number');
assert.equal(typeof mixed[2], 'string');
assert.equal(typeof mixed[3], 'object');
assert.ok(mixed[4] instanceof Date);
assert.ok(mixed[5] instanceof DocumentObjectId);
});
it('boolean', function(){
var Animal = new Schema({
isFerret: { type: Boolean, required: true }
});
assert.strictEqual(Animal.path('isFerret').cast(null), null);
assert.equal(false, Animal.path('isFerret').cast(undefined));
assert.equal(false, Animal.path('isFerret').cast(false));
assert.equal(false, Animal.path('isFerret').cast(0));
assert.equal(false, Animal.path('isFerret').cast('0'));
assert.equal(true, Animal.path('isFerret').cast({}));
assert.equal(true, Animal.path('isFerret').cast(true));
assert.equal(true, Animal.path('isFerret').cast(1));
assert.equal(true, Animal.path('isFerret').cast('1'));
});
});
it('methods declaration', function(){
var a = new Schema;
a.method('test', function(){});
a.method({
a: function(){}
, b: function(){}
});
assert.equal(3, Object.keys(a.methods).length);
});
it('static declaration', function(){
var a = new Schema;
a.static('test', function(){});
a.static({
a: function(){}
, b: function(){}
, c: function(){}
});
assert.equal(Object.keys(a.statics).length, 4)
});
describe('setters', function(){
it('work', function(){
function lowercase (v) {
return v.toLowerCase();
};
var Tobi = new Schema({
name: { type: String, set: lowercase }
});
assert.equal('woot', Tobi.path('name').applySetters('WOOT'));
assert.equal(1, Tobi.path('name').setters.length);
Tobi.path('name').set(function(v){
return v + 'WOOT';
});
assert.equal('wootwoot', Tobi.path('name').applySetters('WOOT'));
assert.equal(2, Tobi.path('name').setters.length);
});
it('order', function(){
function extract (v, self) {
return (v && v._id)
? v._id
: v
};
var Tobi = new Schema({
name: { type: Schema.ObjectId, set: extract }
});
var id = new DocumentObjectId
, sid = id.toString()
, _id = { _id: id };
assert.equal(Tobi.path('name').applySetters(sid, { a: 'b' }).toString(),sid);
assert.equal(Tobi.path('name').applySetters(_id, { a: 'b' }).toString(),sid);
assert.equal(Tobi.path('name').applySetters(id, { a: 'b' }).toString(),sid);
});
it('scope', function(){
function lowercase (v, self) {
assert.equal('b', this.a);
assert.equal('name', self.path);
return v.toLowerCase();
};
var Tobi = new Schema({
name: { type: String, set: lowercase }
});
assert.equal('what', Tobi.path('name').applySetters('WHAT', { a: 'b' }));
});
it('casting', function(){
function last (v) {
assert.equal('number', typeof v);
assert.equal(0, v);
return 'last';
};
function first (v) {
return 0;
};
var Tobi = new Schema({
name: { type: String, set: last }
});
Tobi.path('name').set(first);
assert.equal('last', Tobi.path('name').applySetters('woot'));
});
describe('string', function(){
console.error('\nTODO remove string lowercase/uppercase/trim setters before 3.0\n');
it('lowercase', function(){
var Tobi = new Schema({
name: { type: String, lowercase: true }
});
assert.equal('what', Tobi.path('name').applySetters('WHAT'));
});
it('uppercase', function(){
var Tobi = new Schema({
name: { type: String, uppercase: true }
});
assert.equal('WHAT', Tobi.path('name').applySetters('what'));
});
it('trim', function(){
var Tobi = new Schema({
name: { type: String, uppercase: true, trim: true }
});
assert.equal('WHAT', Tobi.path('name').applySetters(' what '));
});
});
it('applying when none have been defined', function(){
var Tobi = new Schema({
name: String
});
assert.equal('woot', Tobi.path('name').applySetters('woot'));
});
it('assignment of non-functions throw', function(){
var schema = new Schema({ fun: String });
var g, s;
try {
schema.path('fun').set(4);
} catch (err_) {
g = err_;
}
assert.ok(g);
assert.equal(g.message,'A setter must be a function.');
})
});
describe('getters', function(){
it('work', function(){
function woot (v) {
return v + ' woot';
};
var Tobi = new Schema({
name: { type: String, get: woot }
});
assert.equal(1, Tobi.path('name').getters.length);
assert.equal('test woot', Tobi.path('name').applyGetters('test'));
});
it('order', function(){
function format (v, self) {
return v
? '$' + v
: v
};
var Tobi = new Schema({
name: { type: Number, get: format }
});
assert.equal('$30', Tobi.path('name').applyGetters(30, { a: 'b' }));
});
it('scope', function(){
function woot (v, self) {
assert.equal('b', this.a);
assert.equal('name', self.path);
return v.toLowerCase();
};
var Tobi = new Schema({
name: { type: String, get: woot }
});
assert.equal('yep', Tobi.path('name').applyGetters('YEP', { a: 'b' }));
});
it('casting', function(){
function last (v) {
assert.equal('number', typeof v);
assert.equal(0, v);
return 'last';
};
function first (v) {
return 0;
};
var Tobi = new Schema({
name: { type: String, get: last }
});
Tobi.path('name').get(first);
assert.equal('last', Tobi.path('name').applyGetters('woot'));
});
it('applying when none have been defined', function(){
var Tobi = new Schema({
name: String
});
assert.equal('woot', Tobi.path('name').applyGetters('woot'));
});
it('assignment of non-functions throw', function(){
var schema = new Schema({ fun: String });
var g, s;
try {
schema.path('fun').get(true);
} catch (err_) {
g = err_;
}
assert.ok(g);
assert.equal(g.message,'A getter must be a function.');
})
});
describe('hooks', function(){
it('registration', function(){
var Tobi = new Schema();
Tobi.pre('save', function(){});
assert.equal(1, Tobi.callQueue.length);
Tobi.post('save', function(){});
assert.equal(2, Tobi.callQueue.length);
Tobi.pre('save', function(){});
assert.equal(3, Tobi.callQueue.length);
});
});
describe('indexes', function(){
describe('definition', function(){
it('basic', function(){
var Tobi = new Schema({
name: { type: String, index: true }
});
assert.equal(true, Tobi.path('name')._index);
Tobi.path('name').index({ unique: true });
assert.deepEqual(Tobi.path('name')._index, { unique: true });
Tobi.path('name').unique(false);
assert.deepEqual(Tobi.path('name')._index, { unique: false });
var T, i;
T = new Schema({
name: { type: String, sparse: true }
});
assert.deepEqual(T.path('name')._index, { sparse: true });
T = new Schema({
name: { type: String, unique: true }
});
assert.deepEqual(T.path('name')._index, { unique: true });
T = new Schema({
name: { type: String, expires: '1.5m' }
});
assert.deepEqual(T.path('name')._index, { expiresAfterSeconds: 90 });
T = new Schema({
name: { type: String, expires: 200 }
});
assert.deepEqual(T.path('name')._index, { expiresAfterSeconds: 200 });
T = new Schema({
name: { type: String, sparse: true, unique: true }
});
assert.deepEqual(T.path('name')._index, { sparse: true, unique: true });
T = new Schema({
name: { type: String, unique: true, sparse: true }
});
i = T.path('name')._index;
assert.equal(true, i.unique);
assert.equal(true, i.sparse);
T = new Schema({
name: { type: String, index: { sparse: true, unique: true, expiresAfterSeconds: 65 }}
});
i = T.path('name')._index;
assert.equal(true, i.unique);
assert.equal(true, i.sparse);
assert.equal(65, i.expiresAfterSeconds);
T = new Schema({
name: { type: String, index: { sparse: true, unique: true, expires: '24h' }}
});
i = T.path('name')._index;
assert.equal(true, i.unique);
assert.equal(true, i.sparse);
assert.equal(60*60*24, i.expiresAfterSeconds);
})
it('compound', function(){
var Tobi = new Schema({
name: { type: String, index: true }
, last: { type: Number, sparse: true }
, nope: { type: String, index: { background: false }}
});
Tobi.index({ firstname: 1, last: 1 }, { unique: true, expires: '1h' });
Tobi.index({ firstname: 1, nope: 1 }, { unique: true, background: false });
assert.deepEqual(Tobi.indexes(), [
[{ name: 1 }, { background: true }]
, [{ last: 1 }, { sparse: true, background :true }]
, [{ nope: 1 }, { background : false}]
, [{ firstname: 1, last: 1}, {unique: true, expiresAfterSeconds: 60*60, background: true }]
, [{ firstname: 1, nope: 1 }, { unique: true, background: false }]
]);
});
});
});
describe('plugins', function(){
var Tobi = new Schema
, called = false;
Tobi.plugin(function(schema){
assert.equal(schema, Tobi);
called = true;
});
assert.equal(true, called);
});
describe('options', function(){
it('defaults are set', function(){
var Tobi = new Schema();
assert.equal('object', typeof Tobi.options);
assert.equal(true, Tobi.options.safe);
assert.equal(true, Tobi.options.strict);
assert.equal(false, Tobi.options.capped);
assert.equal('__v', Tobi.options.versionKey);
});
it('setting', function(){
var Tobi = new Schema({}, { collection: 'users' });
Tobi.set('a', 'b');
Tobi.set('safe', false);
assert.equal('users', Tobi.options.collection);
assert.equal('b', Tobi.options.a);
assert.equal(Tobi.options.safe, false);
});
});
describe('virtuals', function(){
it('works', function(){
var Contact = new Schema({
firstName: String
, lastName: String
});
Contact
.virtual('fullName')
.get(function () {
return this.get('firstName') + ' ' + this.get('lastName');
})
.set(function (fullName) {
var split = fullName.split(' ');
this.set('firstName', split[0]);
this.set('lastName', split[1]);
});
assert.ok(Contact.virtualpath('fullName') instanceof VirtualType);
});
describe('id', function(){
it('default creation of id can be overridden (gh-298)', function(){
assert.doesNotThrow(function () {
new Schema({ id: String });
});
});
it('disabling', function(){
var schema = new Schema({ name: String }, { noVirtualId: true });
assert.strictEqual(undefined, schema.virtuals.id);
});
});
});
describe('other contexts', function(){
it('work', function(){
var str = 'code = {' +
' name: String' +
', arr1: Array ' +
', arr2: { type: [] }' +
', date: Date ' +
', num: { type: Number }' +
', bool: Boolean' +
', nest: { sub: { type: {}, required: true }}' +
'}';
var script = vm.createScript(str, 'testSchema.vm');
var sandbox = { code: null };
script.runInNewContext(sandbox);
var Ferret = new Schema(sandbox.code);
assert.ok(Ferret.path('nest.sub') instanceof SchemaTypes.Mixed);
assert.ok(Ferret.path('name') instanceof SchemaTypes.String);
assert.ok(Ferret.path('arr1') instanceof SchemaTypes.Array);
assert.ok(Ferret.path('arr2') instanceof SchemaTypes.Array);
assert.ok(Ferret.path('date') instanceof SchemaTypes.Date);
assert.ok(Ferret.path('num') instanceof SchemaTypes.Number);
assert.ok(Ferret.path('bool') instanceof SchemaTypes.Boolean);
});
});
describe('#add()', function(){
it('does not polute existing paths', function(){
var o = { name: String }
var s = new Schema(o);
s.add({ age: Number }, 'name.');
assert.equal(false, ('age' in o.name));
});
it('merging nested objects (gh-662)', function(done){
var db = start();
var MergedSchema = new Schema({
a: {
foo: String
}
});
MergedSchema.add({
a: {
b: {
bar: String
}
}
});
mongoose.model('Merged', MergedSchema);
var Merged = db.model('Merged', 'merged_' + Math.random());
var merged = new Merged({
a: {
foo: 'baz'
, b: {
bar: 'qux'
}
}
});
merged.save(function(err) {
assert.ifError(err);
Merged.findById(merged.id, function(err, found) {
db.close();
assert.ifError(err);
assert.equal(found.a.foo,'baz');
assert.equal(found.a.b.bar,'qux');
done();
});
});
})
});
it('debugging msgs', function(){
var err;
try {
new Schema({ name: { first: null } })
} catch (e) {
err = e;
}
assert.equal(err.message,'Invalid value for schema path `name.first`')
try {
new Schema({ age: undefined })
} catch (e) {
err = e;
}
assert.equal(err.message, 'Invalid value for schema path `age`')
});
describe('construction', function(){
it('array of object literal missing a type is interpreted as DocumentArray', function(){
var goose = new mongoose.Mongoose;
var s = new Schema({
arr: [
{ something: { type: String } }
]
});
assert.ok(s.path('arr') instanceof SchemaTypes.DocumentArray);
var M = goose.model('objectliteralschema', s);
var m = new M({ arr: [ { something: 'wicked this way comes' }] });
assert.equal('wicked this way comes', m.arr[0].something);
assert.ok(m.arr[0]._id);
});
it('array of object literal with type.type is interpreted as DocumentArray', function(){
var goose = new mongoose.Mongoose;
var s = new Schema({
arr: [
{ type: { type: String } }
]
});
assert.ok(s.path('arr') instanceof SchemaTypes.DocumentArray);
var M = goose.model('objectliteralschema2', s);
var m = new M({ arr: [ { type: 'works' }] });
assert.equal('works', m.arr[0].type);
assert.ok(m.arr[0]._id);
});
it('of nested schemas should throw (gh-700)', function(){
var a = new Schema({ title: String })
, err
try {
new Schema({ blah: Boolean, a: a });
} catch (err_) {
err = err_;
}
assert.ok(err);
assert.ok(/Did you try nesting Schemas/.test(err.message));
});
});
describe('conflicting property names', function(){
it('throws', function(){
var child = new Schema({ name: String });
assert.throws(function(){
new Schema({
on: String
, child: [child]
});
}, /`on` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
options: String
});
}, /`options` may not be used as a schema pathname/);
assert.doesNotThrow(function(){
new Schema({
model: String
});
});
assert.throws(function(){
new Schema({
collection: String
});
}, /`collection` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
schema: String
});
}, /`schema` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
db: String
});
}, /`db` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
modelName: String
});
}, /`modelName` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
isNew: String
});
}, /`isNew` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
errors: String
});
}, /`errors` may not be used as a schema pathname/);
assert.throws(function(){
new Schema({
init: String
});
}, /`init` may not be used as a schema pathname/);
assert.doesNotThrow(function(){
Schema({ child: [{parent: String}] });
});
assert.doesNotThrow(function(){
Schema({ child: [{parentArray: String}] });
});
})
})
});