summaryrefslogtreecommitdiff
path: root/node_modules/mongoose/test
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/mongoose/test')
-rw-r--r--node_modules/mongoose/test/collection.test.js116
-rw-r--r--node_modules/mongoose/test/common.js145
-rw-r--r--node_modules/mongoose/test/connection.test.js310
-rw-r--r--node_modules/mongoose/test/crash.test.js36
-rw-r--r--node_modules/mongoose/test/document.strict.test.js178
-rw-r--r--node_modules/mongoose/test/document.test.js912
-rw-r--r--node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js63
-rwxr-xr-xnode_modules/mongoose/test/dropdb.js7
-rw-r--r--node_modules/mongoose/test/index.test.js235
-rw-r--r--node_modules/mongoose/test/model.querying.test.js2352
-rw-r--r--node_modules/mongoose/test/model.ref.test.js1528
-rw-r--r--node_modules/mongoose/test/model.stream.test.js232
-rw-r--r--node_modules/mongoose/test/model.test.js4682
-rw-r--r--node_modules/mongoose/test/model.update.test.js526
-rw-r--r--node_modules/mongoose/test/namedscope.test.js253
-rw-r--r--node_modules/mongoose/test/promise.test.js167
-rw-r--r--node_modules/mongoose/test/query.test.js890
-rw-r--r--node_modules/mongoose/test/schema.onthefly.test.js105
-rw-r--r--node_modules/mongoose/test/schema.test.js978
-rw-r--r--node_modules/mongoose/test/shard.test.js216
-rw-r--r--node_modules/mongoose/test/types.array.test.js551
-rw-r--r--node_modules/mongoose/test/types.buffer.test.js350
-rw-r--r--node_modules/mongoose/test/types.document.test.js197
-rw-r--r--node_modules/mongoose/test/types.documentarray.test.js132
-rw-r--r--node_modules/mongoose/test/types.number.test.js37
-rw-r--r--node_modules/mongoose/test/utils.test.js196
-rw-r--r--node_modules/mongoose/test/zzlast.test.js11
27 files changed, 15405 insertions, 0 deletions
diff --git a/node_modules/mongoose/test/collection.test.js b/node_modules/mongoose/test/collection.test.js
new file mode 100644
index 0000000..54c247c
--- /dev/null
+++ b/node_modules/mongoose/test/collection.test.js
@@ -0,0 +1,116 @@
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , Collection = require('../lib/collection');
+
+module.exports = {
+
+ 'test buffering of commands until connection is established': function(beforeExit){
+ var db = mongoose.createConnection()
+ , collection = db.collection('test-buffering-collection')
+ , connected = false
+ , inserted = false;
+
+ collection.insert({ }, function(){
+ connected.should.be.true;
+ inserted = true;
+ db.close();
+ });
+
+ var uri = 'mongodb://localhost/mongoose_test';
+ db.open(process.env.MONGOOSE_TEST_URI || uri, function(err){
+ connected = !err;
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ inserted.should.be.true;
+ });
+ },
+
+ 'test methods that should throw (unimplemented)': function () {
+ var collection = new Collection('test', mongoose.connection)
+ , thrown = false;
+
+ try {
+ collection.getIndexes();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.update();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.save();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.insert();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.find();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.findOne();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.findAndModify();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+
+ try {
+ collection.ensureIndex();
+ } catch (e) {
+ /unimplemented/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ thrown = false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/common.js b/node_modules/mongoose/test/common.js
new file mode 100644
index 0000000..ffb3783
--- /dev/null
+++ b/node_modules/mongoose/test/common.js
@@ -0,0 +1,145 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('../')
+ , should = require('should')
+ , Table = require('cli-table')
+ , Mongoose = mongoose.Mongoose
+ , Collection = mongoose.Collection
+ , Assertion = should.Assertion
+ , startTime = Date.now()
+ , queryCount = 0
+ , opened = 0
+ , closed = 0;
+
+/**
+ * Override all Collection related queries to keep count
+ */
+
+[ 'ensureIndex'
+ , 'findAndModify'
+ , 'findOne'
+ , 'find'
+ , 'insert'
+ , 'save'
+ , 'update'
+ , 'remove'
+ , 'count'
+ , 'distinct'
+].forEach(function (method) {
+
+ var oldMethod = Collection.prototype[method];
+
+ Collection.prototype[method] = function () {
+ queryCount++;
+ return oldMethod.apply(this, arguments);
+ };
+
+});
+
+/**
+ * Override Collection#onOpen to keep track of connections
+ */
+
+var oldOnOpen = Collection.prototype.onOpen;
+
+Collection.prototype.onOpen = function(){
+ opened++;
+ return oldOnOpen.apply(this, arguments);
+};
+
+/**
+ * Override Collection#onClose to keep track of disconnections
+ */
+
+var oldOnClose = Collection.prototype.onClose;
+
+Collection.prototype.onClose = function(){
+ closed++;
+ return oldOnClose.apply(this, arguments);
+};
+
+/**
+ * Assert that a connection is open or that mongoose connections are open.
+ * Examples:
+ * mongoose.should.be.connected;
+ * db.should.be.connected;
+ *
+ * @api public
+ */
+
+Assertion.prototype.__defineGetter__('connected', function(){
+ if (this.obj instanceof Mongoose)
+ this.obj.connections.forEach(function(connected){
+ c.should.be.connected;
+ });
+ else
+ this.obj.readyState.should.eql(1);
+});
+
+/**
+ * Assert that a connection is closed or that a mongoose connections are closed.
+ * Examples:
+ * mongoose.should.be.disconnected;
+ * db.should.be.disconnected;
+ *
+ * @api public
+ */
+
+Assertion.prototype.__defineGetter__('disconnected', function(){
+ if (this.obj instanceof Mongoose)
+ this.obj.connections.forEach(function(){
+ c.should.be.disconnected;
+ });
+ else
+ this.obj.readyState.should.eql(0);
+});
+
+/**
+ * Create a connection to the test database.
+ * You can set the environmental variable MONGOOSE_TEST_URI to override this.
+ *
+ * @api private
+ */
+
+module.exports = function (options) {
+ var uri;
+
+ if (options && options.uri) {
+ uri = options.uri;
+ delete options.uri;
+ } else {
+ uri = process.env.MONGOOSE_TEST_URI ||
+ 'mongodb://localhost/mongoose_test'
+ }
+
+ return mongoose.createConnection(uri, options);
+};
+
+/**
+ * Provide stats for tests
+ */
+
+process.on('beforeExit', function(){
+ var table = new Table({
+ head: ['Stat', 'Time (ms)']
+ , colWidths: [23, 15]
+ });
+
+ table.push(
+ ['Queries run', queryCount]
+ , ['Time ellapsed', Date.now() - startTime]
+ , ['Connections opened', opened]
+ , ['Connections closed', closed]
+ );
+
+ console.error(table.toString());
+});
+
+/**
+ * Module exports.
+ */
+
+module.exports.mongoose = mongoose;
diff --git a/node_modules/mongoose/test/connection.test.js b/node_modules/mongoose/test/connection.test.js
new file mode 100644
index 0000000..92c162a
--- /dev/null
+++ b/node_modules/mongoose/test/connection.test.js
@@ -0,0 +1,310 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test closing a connection that\'s already closed': function (beforeExit) {
+ var db = mongoose.createConnection()
+ , called = false;
+
+ db.readyState.should.eql(0);
+ db.close(function (err) {
+ should.strictEqual(err, null);
+ called = true;
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'test connection args': function (beforeExit) {
+ var db = mongoose.createConnection('mongodb://localhost/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('mongodb://localhost:27000/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal('27000');
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal('27000');
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { db: { forceServerObjectId: true }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { server: { auto_reconnect: false }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.false;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ var called1 = false;
+ db = mongoose.createConnection('mongodb://aaron:psw@localhost:27000/fake', { server: { auto_reconnect: true }}, function () {
+ called1 = true;
+ });
+ beforeExit(function () {
+ called1.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.close();
+
+ var called2 = false;
+ db = mongoose.createConnection('mongodb://localhost/fake', function () {
+ called2 = true;
+ });
+ beforeExit(function () {
+ called2.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('mongodb:///fake', function (err) {
+ err.message.should.equal('Missing connection hostname.');
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.name);
+ should.strictEqual(undefined, db.host);
+ should.strictEqual(undefined, db.port);
+ db.close();
+
+ db = mongoose.createConnection('mongodb://localhost', function (err) {
+ err.message.should.equal('Missing connection database.');
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.name);
+ should.strictEqual(undefined, db.host);
+ should.strictEqual(undefined, db.port);
+ db.close();
+
+ var called3 = false;
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, { server: { auto_reconnect: false }}, function () {
+ called3 = true;
+ });
+ beforeExit(function () {
+ called3.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.false;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ var called4 = false;
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, function () {
+ called4 = true;
+ });
+ beforeExit(function () {
+ called4.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28000, { server: { auto_reconnect: true }});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28000);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', 28001);
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(28001);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker', { blah: 1 });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.options.blah.should.equal(1);
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ var called5 = false
+ db = mongoose.createConnection('127.0.0.1', 'faker', function () {
+ called5 = true;
+ });
+ beforeExit(function () {
+ called5.should.be.true;
+ });
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ db = mongoose.createConnection('127.0.0.1', 'faker');
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ db.name.should.equal('faker');
+ db.host.should.equal('127.0.0.1');
+ db.port.should.equal(27017);
+ db.close();
+
+ // Test connecting using user/pass in hostname
+ db = mongoose.createConnection('aaron:psw@localhost', 'fake', 27000);
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+
+ // Test connecting using user/pass options
+ db = mongoose.createConnection('localhost', 'fake', 27000, {user: 'aaron', pass: 'psw'});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual('psw', db.pass);
+ should.strictEqual('aaron', db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+
+ // Test connecting using only user option - which shouldn't work
+ db = mongoose.createConnection('localhost', 'fake', 27000, {user: 'no_pass'});
+ db.options.should.be.a('object');
+ db.options.server.should.be.a('object');
+ db.options.server.auto_reconnect.should.be.true;
+ db.options.db.should.be.a('object');
+ db.options.db.forceServerObjectId.should.be.false;
+ should.strictEqual(undefined, db.pass);
+ should.strictEqual(undefined, db.user);
+ db.name.should.equal('fake');
+ db.host.should.equal('localhost');
+ db.port.should.equal(27000);
+ db.close();
+ },
+
+ 'connection.model allows passing a schema': function () {
+ var db = start();
+ var MyModel = db.model('MyModelasdf', new Schema({
+ name: String
+ }));
+
+ MyModel.schema.should.be.an.instanceof(Schema);
+ MyModel.prototype.schema.should.be.an.instanceof(Schema);
+
+ var m = new MyModel({name:'aaron'});
+ m.name.should.eql('aaron');
+ db.close();
+ },
+
+ 'connection error event fires with one listener': function (exit) {
+ var db= start({ uri: 'mongodb://localasdfads/fakeeee'})
+ , called = false;
+ db.on('error', function () {
+ // this callback has no params which triggered the bug #759
+ called = true;
+ });
+ exit(function () {
+ called.should.be.true;
+ });
+ }
+
+};
diff --git a/node_modules/mongoose/test/crash.test.js b/node_modules/mongoose/test/crash.test.js
new file mode 100644
index 0000000..67e6c86
--- /dev/null
+++ b/node_modules/mongoose/test/crash.test.js
@@ -0,0 +1,36 @@
+
+// GH-407
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , should = require('should')
+
+exports['test mongodb crash with invalid objectid string'] = function () {
+ var db = mongoose.createConnection("mongodb://localhost/test-crash");
+
+ var IndexedGuy = new mongoose.Schema({
+ name: { type: String }
+ });
+
+ var Guy = db.model('Guy', IndexedGuy);
+ Guy.find({
+ _id: {
+ $in: [
+ '4e0de2a6ee47bff98000e145',
+ '4e137bd81a6a8e00000007ac',
+ '',
+ '4e0e2ca0795666368603d974']
+ }
+ }, function (err) {
+ db.close();
+
+ // should is acting strange
+ try {
+ should.strictEqual(err.message, "Invalid ObjectId");
+ } catch (er) {
+ console.error(err);
+ throw er;
+ }
+ });
+
+}
diff --git a/node_modules/mongoose/test/document.strict.test.js b/node_modules/mongoose/test/document.strict.test.js
new file mode 100644
index 0000000..f9b8118
--- /dev/null
+++ b/node_modules/mongoose/test/document.strict.test.js
@@ -0,0 +1,178 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , 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
+ , MongooseNumber = mongoose.Types.Number
+ , MongooseArray = mongoose.Types.Array
+ , MongooseError = mongoose.Error;
+
+module.exports = {
+
+ 'strict mode': function(){
+ var db = start();
+
+ var lax = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ });
+
+ var strict = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ }, { strict: true });
+
+ var Lax = db.model('Lax', lax);
+ var Strict = db.model('Strict', strict);
+
+ var l = new Lax({content: 'sample', rouge: 'data'});
+ l._strictMode.should.be.false;
+ l = l.toObject();
+ l.content.should.equal('sample')
+ l.rouge.should.equal('data');
+ should.exist(l.rouge);
+
+ var s = new Strict({content: 'sample', rouge: 'data'});
+ s._strictMode.should.be.true;
+ s = s.toObject();
+ s.should.have.property('ts');
+ s.content.should.equal('sample');
+ s.should.not.have.property('rouge');
+ should.not.exist(s.rouge);
+
+ // instance override
+ var instance = new Lax({content: 'sample', rouge: 'data'}, true);
+ instance._strictMode.should.be.true;
+ instance = instance.toObject();
+ instance.content.should.equal('sample')
+ should.not.exist(instance.rouge);
+ instance.should.have.property('ts')
+
+ // hydrate works as normal, but supports the schema level flag.
+ var s2 = new Strict({content: 'sample', rouge: 'data'}, false);
+ s2._strictMode.should.be.false;
+ s2 = s2.toObject();
+ s2.should.have.property('ts')
+ s2.content.should.equal('sample');
+ s2.should.have.property('rouge');
+ should.exist(s2.rouge);
+
+ // testing init
+ var s3 = new Strict();
+ s3.init({content: 'sample', rouge: 'data'});
+ var s3obj = s3.toObject();
+ s3.content.should.equal('sample');
+ s3.should.not.have.property('rouge');
+ should.not.exist(s3.rouge);
+
+ // strict on create
+ Strict.create({content: 'sample2', rouge: 'data'}, function(err, doc){
+ db.close();
+ doc.content.should.equal('sample2');
+ doc.should.not.have.property('rouge');
+ should.not.exist(doc.rouge);
+ });
+ },
+
+ 'embedded doc strict mode': function(){
+ var db = start();
+
+ var lax = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ });
+
+ var strict = new Schema({
+ ts : { type: Date, default: Date.now }
+ , content: String
+ }, { strict: true });
+
+ var Lax = db.model('EmbeddedLax', new Schema({ dox: [lax] }), 'embdoc'+random());
+ var Strict = db.model('EmbeddedStrict', new Schema({ dox: [strict] }), 'embdoc'+random());
+
+ var l = new Lax({ dox: [{content: 'sample', rouge: 'data'}] });
+ l.dox[0]._strictMode.should.be.false;
+ l = l.dox[0].toObject();
+ l.content.should.equal('sample')
+ l.rouge.should.equal('data');
+ should.exist(l.rouge);
+
+ var s = new Strict({ dox: [{content: 'sample', rouge: 'data'}] });
+ s.dox[0]._strictMode.should.be.true;
+ s = s.dox[0].toObject();
+ s.should.have.property('ts');
+ s.content.should.equal('sample');
+ s.should.not.have.property('rouge');
+ should.not.exist(s.rouge);
+
+ // testing init
+ var s3 = new Strict();
+ s3.init({dox: [{content: 'sample', rouge: 'data'}]});
+ var s3obj = s3.toObject();
+ s3.dox[0].content.should.equal('sample');
+ s3.dox[0].should.not.have.property('rouge');
+ should.not.exist(s3.dox[0].rouge);
+
+ // strict on create
+ Strict.create({dox:[{content: 'sample2', rouge: 'data'}]}, function(err, doc){
+ db.close();
+ doc.dox[0].content.should.equal('sample2');
+ doc.dox[0].should.not.have.property('rouge');
+ should.not.exist(doc.dox[0].rouge);
+ });
+ },
+
+ 'strict mode virtuals': function () {
+ var db = start();
+
+ var getCount = 0
+ , setCount = 0;
+
+ var strictSchema = new Schema({
+ email: String
+ , prop: String
+ }, {strict: true});
+
+ strictSchema
+ .virtual('myvirtual')
+ .get(function() {
+ getCount++;
+ return 'ok';
+ })
+ .set(function(v) {
+ setCount++;
+ this.prop = v;
+ });
+
+ var StrictModel = db.model('StrictVirtual', strictSchema);
+
+ var strictInstance = new StrictModel({
+ email: 'hunter@skookum.com'
+ , myvirtual: 'test'
+ });
+
+ db.close();
+ getCount.should.equal(0);
+ setCount.should.equal(1);
+
+ strictInstance.myvirtual = 'anotherone';
+ var myvirtual = strictInstance.myvirtual;
+
+ getCount.should.equal(1);
+ setCount.should.equal(2);
+ }
+}
diff --git a/node_modules/mongoose/test/document.test.js b/node_modules/mongoose/test/document.test.js
new file mode 100644
index 0000000..7c1b244
--- /dev/null
+++ b/node_modules/mongoose/test/document.test.js
@@ -0,0 +1,912 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId
+ , Document = require('../lib/document')
+ , DocumentObjectId = mongoose.Types.ObjectId;
+
+/**
+ * Test Document constructor.
+ */
+
+function TestDocument () {
+ Document.apply(this, arguments);
+};
+
+/**
+ * Inherits from Document.
+ */
+
+TestDocument.prototype.__proto__ = Document.prototype;
+
+/**
+ * Set a dummy schema to simulate compilation.
+ */
+
+var em = new Schema({ title: String, body: String });
+var schema = TestDocument.prototype.schema = new Schema({
+ test : String
+ , oids : [ObjectId]
+ , numbers : [Number]
+ , nested : {
+ age : Number
+ , cool : ObjectId
+ , deep : { x: String }
+ , path : String
+ , setr : String
+ }
+ , nested2 : {
+ nested: String
+ , yup : {
+ nested : Boolean
+ , yup : String
+ , age : Number
+ }
+ }
+ , em: [em]
+});
+
+schema.virtual('nested.agePlus2').get(function (v) {
+ return this.nested.age + 2;
+});
+schema.virtual('nested.setAge').set(function (v) {
+ this.nested.age = v;
+});
+schema.path('nested.path').get(function (v) {
+ return this.nested.age + (v ? v : '');
+});
+schema.path('nested.setr').set(function (v) {
+ return v + ' setter';
+});
+
+/**
+ * Method subject to hooks. Simply fires the callback once the hooks are
+ * executed.
+ */
+
+TestDocument.prototype.hooksTest = function(fn){
+ fn(null, arguments);
+};
+
+/**
+ * Test.
+ */
+
+module.exports = {
+ 'test shortcut getters': function(){
+ var doc = new TestDocument();
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ }
+ });
+
+ doc.test.should.eql('test');
+ doc.oids.should.be.an.instanceof(Array);
+ (doc.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(doc.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ doc.nested.agePlus2.should.eql(7);
+ doc.nested.path.should.eql('5my path');
+ doc.nested.setAge = 10;
+ (doc.nested.age == 10).should.be.true;
+ doc.nested.setr = 'set it';
+ doc.getValue('nested.setr').should.eql('set it setter');
+
+ var doc2 = new TestDocument();
+ doc2.init({
+ test : 'toop'
+ , oids : []
+ , nested : {
+ age : 2
+ , cool : DocumentObjectId.fromString('4cf70857337498f95900001c')
+ , deep : { x: 'yay' }
+ }
+ });
+
+ doc2.test.should.eql('toop');
+ doc2.oids.should.be.an.instanceof(Array);
+ (doc2.nested.age == 2).should.be.true;
+
+ // GH-366
+ should.strictEqual(doc2.nested.bonk, undefined);
+ should.strictEqual(doc2.nested.nested, undefined);
+ should.strictEqual(doc2.nested.test, undefined);
+ should.strictEqual(doc2.nested.age.test, undefined);
+ should.strictEqual(doc2.nested.age.nested, undefined);
+ should.strictEqual(doc2.oids.nested, undefined);
+ should.strictEqual(doc2.nested.deep.x, 'yay');
+ should.strictEqual(doc2.nested.deep.nested, undefined);
+ should.strictEqual(doc2.nested.deep.cool, undefined);
+ should.strictEqual(doc2.nested2.yup.nested, undefined);
+ should.strictEqual(doc2.nested2.yup.nested2, undefined);
+ should.strictEqual(doc2.nested2.yup.yup, undefined);
+ should.strictEqual(doc2.nested2.yup.age, undefined);
+ doc2.nested2.yup.should.be.a('object');
+
+ doc2.nested2.yup = {
+ age: 150
+ , yup: "Yesiree"
+ , nested: true
+ };
+
+ should.strictEqual(doc2.nested2.nested, undefined);
+ should.strictEqual(doc2.nested2.yup.nested, true);
+ should.strictEqual(doc2.nested2.yup.yup, "Yesiree");
+ (doc2.nested2.yup.age == 150).should.be.true;
+ doc2.nested2.nested = "y";
+ should.strictEqual(doc2.nested2.nested, "y");
+ should.strictEqual(doc2.nested2.yup.nested, true);
+ should.strictEqual(doc2.nested2.yup.yup, "Yesiree");
+ (doc2.nested2.yup.age == 150).should.be.true;
+
+ DocumentObjectId.toString(doc2.nested.cool).should.eql('4cf70857337498f95900001c');
+
+ doc.oids.should.not.equal(doc2.oids);
+ },
+
+ 'test shortcut setters': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'Test'
+ , nested : {
+ age : 5
+ }
+ });
+
+ doc.isModified('test').should.be.false;
+ doc.test = 'Woot';
+ doc.test.should.eql('Woot');
+ doc.isModified('test').should.be.true;
+
+ doc.isModified('nested.age').should.be.false;
+ doc.nested.age = 2;
+ (doc.nested.age == 2).should.be.true;
+ doc.isModified('nested.age').should.be.true;
+ },
+
+ 'test accessor of id': function () {
+ var doc = new TestDocument();
+ doc._id.should.be.an.instanceof(DocumentObjectId);
+ },
+
+ 'test shortcut of id hexString': function () {
+ var doc = new TestDocument()
+ , _id = doc._id.toString();
+ doc.id.should.be.a('string');
+ },
+
+ 'test toObject clone': function(){
+ var doc = new TestDocument();
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : new DocumentObjectId
+ }
+ });
+
+ var copy = doc.toObject();
+
+ copy.test._marked = true;
+ copy.nested._marked = true;
+ copy.nested.age._marked = true;
+ copy.nested.cool._marked = true;
+
+ should.strictEqual(doc._doc.test._marked, undefined);
+ should.strictEqual(doc._doc.nested._marked, undefined);
+ should.strictEqual(doc._doc.nested.age._marked, undefined);
+ should.strictEqual(doc._doc.nested.cool._marked, undefined);
+ },
+
+ 'toObject options': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'test'
+ , oids : []
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ }
+ });
+
+ var clone = doc.toObject({ getters: true, virtuals: false });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('5my path');
+ should.equal(undefined, clone.nested.agePlus2);
+
+ clone = doc.toObject({ virtuals: true });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('my path');
+ clone.nested.agePlus2.should.eql(7);
+
+ clone = doc.toObject({ getters: true });
+
+ clone.test.should.eql('test');
+ clone.oids.should.be.an.instanceof(Array);
+ (clone.nested.age == 5).should.be.true;
+ DocumentObjectId.toString(clone.nested.cool).should.eql('4c6c2d6240ced95d0e00003c');
+ clone.nested.path.should.eql('5my path');
+ clone.nested.agePlus2.should.eql(7);
+ },
+
+ 'test hooks system': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , awaiting = 0
+ , called = false;
+
+ // serial
+ doc.pre('hooksTest', function(next){
+ steps++;
+ setTimeout(function(){
+ // make sure next step hasn't executed yet
+ steps.should.eql(1);
+ next();
+ }, 50);
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ // parallel
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ steps.should.eql(3);
+ setTimeout(function(){
+ steps.should.eql(4);
+ }, 10);
+ setTimeout(function(){
+ steps++;
+ done();
+ }, 110);
+ next();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ setTimeout(function(){
+ steps.should.eql(4);
+ }, 10);
+ setTimeout(function(){
+ steps++;
+ done();
+ }, 110);
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(7);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling next twice doesnt break': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ next();
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling done twice doesnt break': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test that calling done twice on the same doesnt mean completion':
+ function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ });
+
+ doc.hooksTest(function(err){
+ should.strictEqual(err, null);
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(2);
+ called.should.be.false;
+ });
+ },
+
+ 'test hooks system errors from a serial hook': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next();
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ next(new Error);
+ });
+
+ doc.pre('hooksTest', function(next){
+ steps++;
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(3);
+ called.should.be.true;
+ });
+ },
+
+ 'test hooks system erros from last serial hook': function(beforeExit){
+ var doc = new TestDocument()
+ , called = false;
+
+ doc.pre('hooksTest', function(next){
+ next(new Error());
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ called = true;
+ });
+
+ beforeExit(function(){
+ called.should.be.true;
+ });
+ },
+
+ 'test mutating incoming args via middleware': function (beforeExit) {
+ var doc = new TestDocument();
+
+ doc.pre('set', function(next, path, val){
+ next(path, 'altered-' + val);
+ });
+
+ doc.set('test', 'me');
+
+ beforeExit(function(){
+ doc.test.should.equal('altered-me');
+ });
+ },
+
+ 'test hooks system errors from a parallel hook': function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', true, function(next, done){
+ steps++;
+ next();
+ done(new Error);
+ });
+
+ doc.hooksTest(function(err){
+ err.should.be.an.instanceof(Error);
+ steps++;
+ called = true;
+ });
+
+ beforeExit(function(){
+ steps.should.eql(4);
+ called.should.be.true;
+ });
+ },
+
+ 'test that its not necessary to call the last next in the parallel chain':
+ function(beforeExit){
+ var doc = new TestDocument()
+ , steps = 0
+ , called = false;
+
+ doc.pre('hooksTest', function(next, done){
+ next();
+ done();
+ });
+
+ doc.pre('hooksTest', function(next, done){
+ done();
+ });
+
+ doc.hooksTest(function(){
+ called = true;
+ });
+
+ beforeExit(function(){
+ called.should.be.true;
+ });
+ },
+
+ 'test passing two arguments to a method subject to hooks and return value':
+ function (beforeExit) {
+ var doc = new TestDocument()
+ , called = false;
+
+ doc.pre('hooksTest', function (next) {
+ next();
+ });
+
+ doc.hooksTest(function (err, args) {
+ args.should.have.length(2);
+ args[1].should.eql('test');
+ called = true;
+ }, 'test')
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ // gh-746
+ 'hooking set works with document arrays': function () {
+ var db = start();
+
+ var child = new Schema({ text: String });
+
+ child.pre('set', function (next, path, value, type) {
+ next(path, value, type);
+ });
+
+ var schema = new Schema({
+ name: String
+ , e: [child]
+ });
+
+ var S = db.model('docArrayWithHookedSet', schema);
+
+ var s = new S({ name: "test" });
+ s.e = [{ text: 'hi' }];
+ s.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ },
+
+ 'test jsonifying an object': function () {
+ var doc = new TestDocument({ test: 'woot' })
+ , oidString = DocumentObjectId.toString(doc._id);
+
+ // convert to json string
+ var json = JSON.stringify(doc);
+
+ // parse again
+ var obj = JSON.parse(json);
+
+ obj.test.should.eql('woot');
+ obj._id.should.eql(oidString);
+ },
+
+ 'toObject should not set undefined values to null': function () {
+ var doc = new TestDocument()
+ , obj = doc.toObject();
+
+ delete obj._id;
+ obj.should.eql({ numbers: [], oids: [], em: [] });
+ },
+
+ // GH-209
+ 'MongooseErrors should be instances of Error': function () {
+ var MongooseError = require('../lib/error')
+ , err = new MongooseError("Some message");
+ err.should.be.an.instanceof(Error);
+ },
+ 'ValidationErrors should be instances of Error': function () {
+ var ValidationError = Document.ValidationError
+ , err = new ValidationError(new TestDocument);
+ err.should.be.an.instanceof(Error);
+ },
+
+ 'methods on embedded docs should work': function () {
+ var db = start()
+ , ESchema = new Schema({ name: String })
+
+ ESchema.methods.test = function () {
+ return this.name + ' butter';
+ }
+ ESchema.statics.ten = function () {
+ return 10;
+ }
+
+ var E = db.model('EmbeddedMethodsAndStaticsE', ESchema);
+ var PSchema = new Schema({ embed: [ESchema] });
+ var P = db.model('EmbeddedMethodsAndStaticsP', PSchema);
+
+ var p = new P({ embed: [{name: 'peanut'}] });
+ should.equal('function', typeof p.embed[0].test);
+ should.equal('function', typeof E.ten);
+ p.embed[0].test().should.equal('peanut butter');
+ E.ten().should.equal(10);
+
+ // test push casting
+ p = new P;
+ p.embed.push({name: 'apple'});
+ should.equal('function', typeof p.embed[0].test);
+ should.equal('function', typeof E.ten);
+ p.embed[0].test().should.equal('apple butter');
+
+ db.close();
+ },
+
+ 'setting a positional path does not cast value to array': function () {
+ var doc = new TestDocument;
+ doc.init({ numbers: [1,3] });
+ doc.numbers[0].should.eql(1);
+ doc.numbers[1].valueOf().should.eql(3);
+ doc.set('numbers.1', 2);
+ doc.numbers[0].should.eql(1);
+ doc.numbers[1].valueOf().should.eql(2);
+ },
+
+ 'no maxListeners warning should occur': function () {
+ var db = start();
+
+ var traced = false;
+ var trace = console.trace;
+
+ console.trace = function () {
+ traced = true;
+ console.trace = trace;
+ }
+
+ var schema = new Schema({
+ title: String
+ , embed1: [new Schema({name:String})]
+ , embed2: [new Schema({name:String})]
+ , embed3: [new Schema({name:String})]
+ , embed4: [new Schema({name:String})]
+ , embed5: [new Schema({name:String})]
+ , embed6: [new Schema({name:String})]
+ , embed7: [new Schema({name:String})]
+ , embed8: [new Schema({name:String})]
+ , embed9: [new Schema({name:String})]
+ , embed10: [new Schema({name:String})]
+ , embed11: [new Schema({name:String})]
+ });
+
+ var S = db.model('noMaxListeners', schema);
+
+ var s = new S({ title: "test" });
+ db.close();
+ traced.should.be.false
+ },
+
+ 'isSelected': function () {
+ var doc = new TestDocument();
+
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ , em: [{ title: 'gocars' }]
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true; // even if no data
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true; // not a path
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true; // not a path
+ doc.isSelected('noway').should.be.true; // not a path
+ doc.isSelected('notapath').should.be.true; // not a path but in the _doc
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true; // not a path
+
+ var selection = {
+ 'test': 1
+ , 'numbers': 1
+ , 'nested.deep': 1
+ , 'oids': 1
+ }
+
+ doc = new TestDocument(undefined, selection);
+
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ deep : { x: 'a string' }
+ }
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true; // even if no data
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.false;
+ doc.isSelected('nested.cool').should.be.false;
+ doc.isSelected('nested.path').should.be.false;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.false;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.false;
+ doc.isSelected('notapath').should.be.false;
+ doc.isSelected('em').should.be.false;
+ doc.isSelected('em.title').should.be.false;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ 'em.title': 1
+ }
+
+ doc = new TestDocument(undefined, selection);
+
+ doc.init({
+ em: [{ title: 'one' }]
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.false;
+ doc.isSelected('numbers').should.be.false;
+ doc.isSelected('oids').should.be.false;
+ doc.isSelected('nested').should.be.false;
+ doc.isSelected('nested.age').should.be.false;
+ doc.isSelected('nested.cool').should.be.false;
+ doc.isSelected('nested.path').should.be.false;
+ doc.isSelected('nested.deep').should.be.false;
+ doc.isSelected('nested.nope').should.be.false;
+ doc.isSelected('nested.deep.x').should.be.false;
+ doc.isSelected('nested.deep.x.no').should.be.false;
+ doc.isSelected('nested.deep.y').should.be.false;
+ doc.isSelected('noway').should.be.false;
+ doc.isSelected('notapath').should.be.false;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ 'em': 0
+ }
+
+ doc = new TestDocument(undefined, selection);
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ });
+
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.false;
+ doc.isSelected('em.title').should.be.false;
+ doc.isSelected('em.body').should.be.false;
+ doc.isSelected('em.nonpath').should.be.false;
+
+ var selection = {
+ '_id': 0
+ }
+
+ doc = new TestDocument(undefined, selection);
+ doc.init({
+ test : 'test'
+ , numbers : [4,5,6,7]
+ , nested : {
+ age : 5
+ , cool : DocumentObjectId.fromString('4c6c2d6240ced95d0e00003c')
+ , path : 'my path'
+ , deep : { x: 'a string' }
+ }
+ , notapath: 'i am not in the schema'
+ });
+
+ doc.isSelected('_id').should.be.false;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+
+ doc = new TestDocument({ test: 'boom' });
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true;
+
+ doc = new TestDocument({ test: 'boom' }, true);
+ doc.isSelected('_id').should.be.true;
+ doc.isSelected('test').should.be.true;
+ doc.isSelected('numbers').should.be.true;
+ doc.isSelected('oids').should.be.true;
+ doc.isSelected('nested').should.be.true;
+ doc.isSelected('nested.age').should.be.true;
+ doc.isSelected('nested.cool').should.be.true;
+ doc.isSelected('nested.path').should.be.true;
+ doc.isSelected('nested.deep').should.be.true;
+ doc.isSelected('nested.nope').should.be.true;
+ doc.isSelected('nested.deep.x').should.be.true;
+ doc.isSelected('nested.deep.x.no').should.be.true;
+ doc.isSelected('nested.deep.y').should.be.true;
+ doc.isSelected('noway').should.be.true;
+ doc.isSelected('notapath').should.be.true;
+ doc.isSelected('em').should.be.true;
+ doc.isSelected('em.title').should.be.true;
+ doc.isSelected('em.body').should.be.true;
+ doc.isSelected('em.nonpath').should.be.true;
+ },
+
+ 'unselected required fields should pass validation': function () {
+ var db = start()
+ , Tschema = new Schema({ name: String, req: { type: String, required: true }})
+ , T = db.model('unselectedRequiredFieldValidation', Tschema);
+
+ var t = new T({ name: 'teeee', req: 'i am required' });
+ t.save(function (err) {
+ should.strictEqual(null, err);
+ T.findById(t).select('name').exec(function (err, t) {
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, t.req);
+ t.name = 'wooo';
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t).select('name').exec(function (err, t) {
+ should.strictEqual(null, err);
+ t.req = undefined;
+ t.save(function (err) {
+ err = String(err);
+ var invalid = /Validator "required" failed for path req/.test(err);
+ invalid.should.be.true;
+ t.req = 'it works again'
+ t.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js b/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js
new file mode 100644
index 0000000..2b3d13d
--- /dev/null
+++ b/node_modules/mongoose/test/drivers/node-mongodb-native/collection.test.js
@@ -0,0 +1,63 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('../../common')
+ , mongoose = start.mongoose
+ , should = require('should')
+ , Schema = mongoose.Schema;
+
+/**
+ * Setup.
+ */
+
+mongoose.model('NativeDriverTest', new Schema({
+ title: String
+}));
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that trying to implement a sparse index works': function () {
+ var db = start()
+ , NativeTestCollection = db.model('NativeDriverTest');
+
+ NativeTestCollection.collection.ensureIndex({ title: 1 }, { sparse: true }, function (err) {
+ should.strictEqual(!!err, false);
+ NativeTestCollection.collection.getIndexes(function (err, indexes) {
+ db.close();
+ should.strictEqual(!!err, false);
+ indexes.should.be.instanceof(Object);
+ indexes['title_1'].should.eql([['title', 1]]);
+ });
+ });
+ },
+
+ 'test that the -native traditional ensureIndex spec syntax for fields works': function () {
+ var db = start()
+ , NativeTestCollection = db.model('NativeDriverTest');
+
+ NativeTestCollection.collection.ensureIndex([['a', 1]], function () {
+ db.close();
+ });
+ },
+
+ 'unique index fails passes error': function () {
+ var db = start()
+ , schema = new Schema({ title: String })
+ , NativeTestCollection = db.model('NativeDriverTestUnique', schema)
+
+ NativeTestCollection.create({ title: 'x' }, {title:'x'}, function (err) {
+ should.strictEqual(!!err, false);
+
+ NativeTestCollection.collection.ensureIndex({ title: 1 }, { unique: true, safe: true }, function (err) {
+ db.close();
+ ;/E11000 duplicate key error index/.test(err.message).should.equal(true);
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/dropdb.js b/node_modules/mongoose/test/dropdb.js
new file mode 100755
index 0000000..0b806e7
--- /dev/null
+++ b/node_modules/mongoose/test/dropdb.js
@@ -0,0 +1,7 @@
+var start = require('./common')
+var db = start();
+db.on('open', function () {
+ db.db.dropDatabase(function () {
+ process.exit();
+ });
+});
diff --git a/node_modules/mongoose/test/index.test.js b/node_modules/mongoose/test/index.test.js
new file mode 100644
index 0000000..db0625d
--- /dev/null
+++ b/node_modules/mongoose/test/index.test.js
@@ -0,0 +1,235 @@
+
+var url = require('url')
+ , start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , Mongoose = mongoose.Mongoose
+ , Schema = mongoose.Schema;
+
+module.exports = {
+
+ 'test connecting to the demo database': function(beforeExit){
+ var db = start()
+ , connected = false;
+
+ db.on('open', function(){
+ connected = true;
+ db.close();
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ });
+ },
+
+ 'test default connection': function(beforeExit){
+ var db = mongoose.connection
+ , uri = 'mongodb://localhost/mongoose_test'
+ , connected = false;
+
+ mongoose.connect(process.env.MONGOOSE_TEST_URI || uri);
+ db.on('open', function(){
+ connected = true;
+ db.close();
+ });
+
+ beforeExit(function(){
+ connected.should.be.true;
+ });
+ },
+
+ 'test setting options': function(){
+ var mongoose = new Mongoose();
+
+ mongoose.set('a', 'b');
+ mongoose.set('long option', 'c');
+
+ mongoose.get('a').should.eql('b');
+ mongoose.set('a').should.eql('b');
+ mongoose.get('long option').should.eql('c');
+ },
+
+ 'test declaring global plugins': function (beforeExit) {
+ var mong = new Mongoose()
+ , schema = new Schema()
+ , called = 0;
+
+ mong.plugin(function (s) {
+ s.should.equal(schema);
+ called++;
+ });
+
+ schema.plugin(function (s) {
+ s.should.equal(schema);
+ called++;
+ });
+
+ mong.model('GlobalPlugins', schema);
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test disconnection of all connections': function (beforeExit) {
+ var mong = new Mongoose()
+ , uri = 'mongodb://localhost/mongoose_test'
+ , connections = 0
+ , disconnections = 0;
+
+ mong.connect(process.env.MONGOOSE_TEST_URI || uri);
+ var db = mong.connection;
+
+ db.on('open', function(){
+ connections++;
+ });
+
+ db.on('close', function () {
+ disconnections++;
+ });
+
+ var db2 = mong.createConnection(process.env.MONGOOSE_TEST_URI || uri);
+
+ db2.on('open', function () {
+ connections++;
+ });
+
+ db2.on('close', function () {
+ disconnections++;
+ });
+
+ mong.disconnect();
+
+ beforeExit(function () {
+ connections.should.eql(2);
+ disconnections.should.eql(2);
+ });
+ },
+
+ 'test disconnection of all connections callback': function (beforeExit) {
+ var mong = new Mongoose()
+ , uri = 'mongodb://localhost/mongoose_test'
+ , called = false;
+
+ mong.connect(process.env.MONGOOSE_TEST_URI || uri);
+
+ mong.connection.on('open', function () {
+ mong.disconnect(function () {
+ called = true;
+ });
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'try accessing a model that hasn\'t been defined': function () {
+ var mong = new Mongoose()
+ , thrown = false;
+
+ try {
+ mong.model('Test');
+ } catch (e) {
+ /hasn't been registered/.test(e.message).should.be.true;
+ thrown = true;
+ }
+
+ thrown.should.be.true;
+ },
+
+ 'test connecting with a signature of host, database, function': function (){
+ var mong = new Mongoose()
+ , uri = process.env.MONGOOSE_TEST_URI || 'mongodb://localhost/mongoose_test';
+
+ uri = url.parse(uri);
+
+ mong.connect(uri.hostname, uri.pathname.substr(1), function (err) {
+ should.strictEqual(err, null);
+ mong.connection.close();
+ });
+ },
+
+ 'test connecting to a replica set': function () {
+ var uri = process.env.MONGOOSE_SET_TEST_URI;
+
+ if (!uri) {
+ console.log('\033[30m', '\n', 'You\'re not testing replica sets!'
+ , '\n', 'Please set the MONGOOSE_SET_TEST_URI env variable.', '\n'
+ , 'e.g: `mongodb://localhost:27017/db,mongodb://localhost…`', '\n'
+ , '\033[39m');
+ return;
+ }
+
+ var mong = new Mongoose();
+
+ mong.connectSet(uri, function (err) {
+ should.strictEqual(err, null);
+
+ mong.model('Test', new mongoose.Schema({
+ test: String
+ }));
+
+ var Test = mong.model('Test')
+ , test = new Test();
+
+ test.test = 'aa';
+ test.save(function (err) {
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.test.should.eql('aa');
+
+ mong.connection.close();
+ });
+ });
+ });
+ },
+
+ 'test initializing a new Connection to a replica set': function () {
+ var uri = process.env.MONGOOSE_SET_TEST_URI;
+
+ if (!uri) return;
+
+ var mong = new Mongoose(true);
+
+ var conn = mong.createSetConnection(uri, function (err) {
+ should.strictEqual(err, null);
+
+ mong.model('ReplSetTwo', new mongoose.Schema({
+ test: String
+ }));
+
+ var Test = conn.model('ReplSetTwo')
+ , test = new Test();
+
+ test.test = 'aa';
+ test.save(function (err) {
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.test.should.eql('aa');
+
+ conn.close();
+ });
+ });
+ });
+ },
+
+ 'test public exports': function () {
+ mongoose.version.should.be.a('string');
+ mongoose.Collection.should.be.a('function');
+ mongoose.Connection.should.be.a('function');
+ mongoose.Schema.should.be.a('function');
+ mongoose.SchemaType.should.be.a('function');
+ mongoose.Query.should.be.a('function');
+ mongoose.Promise.should.be.a('function');
+ mongoose.Model.should.be.a('function');
+ mongoose.Document.should.be.a('function');
+ }
+
+};
diff --git a/node_modules/mongoose/test/model.querying.test.js b/node_modules/mongoose/test/model.querying.test.js
new file mode 100644
index 0000000..2f5d53e
--- /dev/null
+++ b/node_modules/mongoose/test/model.querying.test.js
@@ -0,0 +1,2352 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ObjectId = Schema.ObjectId
+ , MongooseBuffer = mongoose.Types.Buffer
+ , DocumentObjectId = mongoose.Types.ObjectId;
+
+/**
+ * Setup.
+ */
+
+var Comments = new Schema();
+
+Comments.add({
+ title : String
+ , date : Date
+ , body : String
+ , comments : [Comments]
+});
+
+var BlogPostB = new Schema({
+ title : String
+ , author : String
+ , slug : String
+ , date : Date
+ , meta : {
+ date : Date
+ , visitors : Number
+ }
+ , published : Boolean
+ , mixed : {}
+ , numbers : [Number]
+ , tags : [String]
+ , sigs : [Buffer]
+ , owners : [ObjectId]
+ , comments : [Comments]
+ , def : { type: String, default: 'kandinsky' }
+});
+
+mongoose.model('BlogPostB', BlogPostB);
+var collection = 'blogposts_' + random();
+
+var ModSchema = new Schema({
+ num: Number
+});
+mongoose.model('Mod', ModSchema);
+
+var geoSchema = new Schema({ loc: { type: [Number], index: '2d'}});
+
+module.exports = {
+
+ 'test that find returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ // query
+ BlogPostB.find({}).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.find({}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.find({}, []).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.find({}, {}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.find({}, [], {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that findOne returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ // query
+ BlogPostB.findOne({}).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.findOne({}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.findOne({}, []).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.findOne({}, {}, {}).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.findOne({}, [], {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that an empty find does not hang': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+
+ function fn () {
+ db.close();
+ };
+
+ BlogPostB.find({}, fn);
+ },
+
+ 'test that a query is executed when a callback is passed': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 5
+ , q = { _id: new DocumentObjectId }; // make sure the query is fast
+
+ function fn () {
+ --count || db.close();
+ };
+
+ // query
+ BlogPostB.find(q, fn).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.find(q, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.find(q, [], fn).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.find(q, {}, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.find(q, [], {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that query is executed where a callback for findOne': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 5
+ , q = { _id: new DocumentObjectId }; // make sure the query is fast
+
+ function fn () {
+ --count || db.close();
+ };
+
+ // query
+ BlogPostB.findOne(q, fn).should.be.an.instanceof(Query);
+
+ // query, fields
+ BlogPostB.findOne(q, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array)
+ BlogPostB.findOne(q, [], fn).should.be.an.instanceof(Query);
+
+ // query, fields, options
+ BlogPostB.findOne(q, {}, {}, fn).should.be.an.instanceof(Query);
+
+ // query, fields (array), options
+ BlogPostB.findOne(q, [], {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that count returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.count({}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that count Query executes when you pass a callback': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , pending = 2
+
+ function fn () {
+ if (--pending) return;
+ db.close();
+ };
+
+ BlogPostB.count({}, fn).should.be.an.instanceof(Query);
+ BlogPostB.count(fn).should.be.an.instanceof(Query);
+ },
+
+ 'test that distinct returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.distinct('title', {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that distinct Query executes when you pass a callback': function () {
+ var db = start();
+ var Address = new Schema({ zip: String });
+ Address = db.model('Address', Address, 'addresses_' + random());
+
+ Address.create({ zip: '10010'}, { zip: '10010'}, { zip: '99701'}, function (err, a1, a2, a3) {
+ should.strictEqual(null, err);
+ var query = Address.distinct('zip', {}, function (err, results) {
+ should.strictEqual(null, err);
+ results.should.eql(['10010', '99701']);
+ db.close();
+ });
+ query.should.be.an.instanceof(Query);
+ });
+ },
+
+
+ 'test that update returns a Query': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.update({}, {}).should.be.an.instanceof(Query);
+ BlogPostB.update({}, {}, {}).should.be.an.instanceof(Query);
+
+ db.close();
+ },
+
+ 'test that update Query executes when you pass a callback': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , count = 2;
+
+ function fn () {
+ --count || db.close();
+ };
+
+ BlogPostB.update({title: random()}, {}, fn).should.be.an.instanceof(Query);
+
+ BlogPostB.update({title: random()}, {}, {}, fn).should.be.an.instanceof(Query);
+ },
+
+ 'test finding a document': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ title: title }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.get('title').should.eql(title);
+ doc.isNew.should.be.false;
+
+ db.close();
+ });
+ });
+ },
+
+ 'test finding a document byId': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Edwald ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var pending = 2;
+
+ BlogPostB.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.should.be.an.instanceof(BlogPostB);
+ doc.get('title').should.eql(title);
+ --pending || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id').toHexString(), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.should.be.an.instanceof(BlogPostB);
+ doc.get('title').should.eql(title);
+ --pending || db.close();
+ });
+ });
+ },
+
+ 'test finding documents': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ title: title }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.should.have.length(2);
+
+ docs[0].get('title').should.eql(title);
+ docs[0].isNew.should.be.false;
+
+ docs[1].get('title').should.eql(title);
+ docs[1].isNew.should.be.false;
+
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding documents where an array that contains one specific member': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({numbers: [100, 101, 102]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({numbers: 100}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test counting documents': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Wooooot ' + random();
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = new BlogPostB();
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.count({ title: title }, function (err, count) {
+ should.strictEqual(err, null);
+
+ count.should.be.a('number');
+ count.should.eql(2);
+
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test query casting': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , title = 'Loki ' + random();
+
+ var post = new BlogPostB()
+ , id = DocumentObjectId.toString(post.get('_id'));
+
+ post.set('title', title);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ _id: id }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.get('title').should.equal(title);
+ db.close();
+ });
+ });
+ },
+
+ 'test a query that includes a casting error': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.find({ date: 'invalid date' }, function (err) {
+ err.should.be.an.instanceof(Error);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test findOne queries that require casting for $modifiers': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: -10
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ 'meta.visitors': { $gt: '-20', $lt: -1 } },
+ function (err, found) {
+ found.get('meta.visitors')
+ .valueOf().should.equal(post.get('meta.visitors').valueOf());
+ found.id;
+ found.get('_id').should.eql(post.get('_id'));
+ db.close();
+ });
+ });
+ },
+
+ 'test find queries that require casting for $modifiers': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: -75
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ 'meta.visitors': { $gt: '-100', $lt: -50 } },
+ function (err, found) {
+ should.strictEqual(err, null);
+
+ found.should.have.length(1);
+ found[0].get('_id').should.eql(post.get('_id'));
+ found[0].get('meta.visitors').valueOf()
+ .should.equal(post.get('meta.visitors').valueOf());
+ db.close();
+ });
+ });
+ },
+
+ // GH-199
+ 'test find queries where $in cast the values wherein the array': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB()
+ , id = DocumentObjectId.toString(post._id);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ _id: { $in: [id] } }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ DocumentObjectId.toString(doc._id).should.eql(id);
+ db.close();
+ });
+ });
+ },
+
+ // GH-232
+ 'test find queries where $nin cast the values wherein the array': function () {
+ var db = start()
+ , NinSchema = new Schema({
+ num: Number
+ });
+ mongoose.model('Nin', NinSchema);
+ var Nin = db.model('Nin', 'nins_' + random());
+ Nin.create({ num: 1 }, function (err, one) {
+ should.strictEqual(err, null);
+ Nin.create({ num: 2 }, function (err, two) {
+ should.strictEqual(err, null);
+ Nin.create({num: 3}, function (err, three) {
+ should.strictEqual(err, null);
+ Nin.find({ num: {$nin: [2]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test find queries with $ne with single value against array': function () {
+ var db = start();
+ var schema = new Schema({
+ ids: [Schema.ObjectId]
+ , b: Schema.ObjectId
+ });
+
+ var NE = db.model('NE_Test', schema, 'nes__' + random());
+
+ var id1 = new DocumentObjectId;
+ var id2 = new DocumentObjectId;
+ var id3 = new DocumentObjectId;
+ var id4 = new DocumentObjectId;
+
+ NE.create({ ids: [id1, id4], b: id3 }, function (err, ne1) {
+ should.strictEqual(err, null);
+ NE.create({ ids: [id2, id4], b: id3 },function (err, ne2) {
+ should.strictEqual(err, null);
+
+ var query = NE.find({ 'b': id3.toString(), 'ids': { $ne: id1 }});
+ query.run(function (err, nes1) {
+ should.strictEqual(err, null);
+ nes1.length.should.eql(1);
+
+ NE.find({ b: { $ne: [1] }}, function (err, nes2) {
+ err.message.should.eql("Invalid ObjectId");
+
+ NE.find({ b: { $ne: 4 }}, function (err, nes3) {
+ err.message.should.eql("Invalid ObjectId");
+
+ NE.find({ b: id3, ids: { $ne: id4 }}, function (err, nes4) {
+ db.close();
+ should.strictEqual(err, null);
+ nes4.length.should.eql(0);
+ });
+ });
+ });
+ });
+
+ });
+ });
+
+ },
+
+ 'test for findById where partial initialization': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , queries = 5;
+
+ var post = new BlogPostB();
+
+ post.title = 'hahaha';
+ post.slug = 'woot';
+ post.meta.visitors = 53;
+ post.tags = ['humidity', 'soggy'];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.true;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.true;
+ doc.meta.visitors.valueOf().should.equal(53);
+ doc.tags.length.should.equal(2);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), ['title'], function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), { slug: 0 }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.true;
+ doc.meta.visitors.valueOf().should.equal(53);
+ doc.tags.length.should.equal(2);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), { title:1 }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.true;
+ doc.isInit('slug').should.be.false;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+
+ BlogPostB.findById(post.get('_id'), ['slug'], function (err, doc) {
+ should.strictEqual(err, null);
+ doc.isInit('title').should.be.false;
+ doc.isInit('slug').should.be.true;
+ doc.isInit('date').should.be.false;
+ doc.isInit('meta.visitors').should.be.false;
+ should.strictEqual(undefined, doc.meta.visitors);
+ should.strictEqual(undefined, doc.tags);
+ --queries || db.close();
+ });
+ });
+ },
+
+ 'findOne where subset of fields excludes _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({title: 'subset 1'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({title: 'subset 1'}, {title: 1, _id: 0}, function (err, found) {
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found._id);
+ found.title.should.equal('subset 1');
+ db.close();
+ });
+ });
+ },
+
+ 'test find where subset of fields, excluding _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+ BlogPostB.create({title: 'subset 1', author: 'me'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: 'subset 1'}, {title: 1, _id: 0}, function (err, found) {
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found[0]._id);
+ found[0].title.should.equal('subset 1');
+ should.strictEqual(undefined, found[0].def);
+ should.strictEqual(undefined, found[0].author);
+ should.strictEqual(false, Array.isArray(found[0].comments));
+ db.close();
+ });
+ });
+ },
+
+ // gh-541
+ 'find subset of fields excluding embedded doc _id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'LOTR', comments: [{ title: ':)' }]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.find({_id: created}, { _id: 0, 'comments._id': 0 }, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, found[0]._id);
+ found[0].title.should.equal('LOTR');
+ should.strictEqual('kandinsky', found[0].def);
+ should.strictEqual(undefined, found[0].author);
+ should.strictEqual(true, Array.isArray(found[0].comments));
+ found[0].comments.length.should.equal(1);
+ found[0].comments[0].title.should.equal(':)');
+ should.strictEqual(undefined, found[0].comments[0]._id);
+ // gh-590
+ should.strictEqual(null, found[0].comments[0].id);
+ });
+ });
+ },
+
+
+ 'exluded fields should be undefined': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , date = new Date
+
+ BlogPostB.create({title: 'subset 1', author: 'me', meta: { date: date }}, function (err, created) {
+ should.strictEqual(err, null);
+ var id = created.id;
+ BlogPostB.findById(created.id, {title: 0, 'meta.date': 0, owners: 0}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ should.strictEqual(undefined, found.title);
+ should.strictEqual('kandinsky', found.def);
+ should.strictEqual('me', found.author);
+ should.strictEqual(true, Array.isArray(found.comments));
+ should.equal(undefined, found.meta.date);
+ found.comments.length.should.equal(0);
+ should.equal(undefined, found.owners);
+ });
+ });
+ },
+
+ 'exluded fields should be undefined and defaults applied to other fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , id = new DocumentObjectId
+ , date = new Date
+
+ BlogPostB.collection.insert({ _id: id, title: 'hahaha1', meta: { date: date }}, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(id, {title: 0}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found._id.should.eql(id);
+ should.strictEqual(undefined, found.title);
+ should.strictEqual('kandinsky', found.def);
+ should.strictEqual(undefined, found.author);
+ should.strictEqual(true, Array.isArray(found.comments));
+ should.equal(date.toString(), found.meta.date.toString());
+ found.comments.length.should.equal(0);
+ });
+ });
+ },
+
+ 'test for find where partial initialization': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , queries = 4;
+
+ var post = new BlogPostB();
+
+ post.title = 'hahaha';
+ post.slug = 'woot';
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ _id: post.get('_id') }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.true;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual('kandinsky', docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, ['title'], function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.false;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, { slug: 0, def: 0 }, function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.true;
+ docs[0].isInit('slug').should.be.false;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+
+ BlogPostB.find({ _id: post.get('_id') }, ['slug'], function (err, docs) {
+ should.strictEqual(err, null);
+ docs[0].isInit('title').should.be.false;
+ docs[0].isInit('slug').should.be.true;
+ docs[0].isInit('date').should.be.false;
+ should.strictEqual(undefined, docs[0].def);
+ --queries || db.close();
+ });
+ });
+ },
+
+ // GH-204
+ 'test query casting when finding by Date': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection);
+
+ var post = new P;
+
+ post.meta.date = new Date();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ P.findOne({ _id: post._id, 'meta.date': { $lte: Date.now() } }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ DocumentObjectId.toString(doc._id).should.eql(DocumentObjectId.toString(post._id));
+ doc.meta.date = null;
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+ P.findById(doc._id, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(doc.meta.date, null);
+ });
+ });
+ });
+ });
+ },
+
+ // gh-523
+ 'null boolean default is allowed': function () {
+ var db = start()
+ , s1 = new Schema({ b: { type: Boolean, default: null }})
+ , M1 = db.model('NullDateDefaultIsAllowed1', s1)
+ , s2 = new Schema({ b: { type: Boolean, default: false }})
+ , M2 = db.model('NullDateDefaultIsAllowed2', s2)
+ , s3 = new Schema({ b: { type: Boolean, default: true }})
+ , M3 = db.model('NullDateDefaultIsAllowed3', s3)
+
+ db.close();
+
+ var m1 = new M1;
+ should.strictEqual(null, m1.b);
+ var m2 = new M2;
+ should.strictEqual(false, m2.b);
+ var m3 = new M3;
+ should.strictEqual(true, m3.b);
+ },
+
+ // GH-220
+ 'test querying if an array contains at least a certain single member': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB();
+
+ post.tags.push('cat');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({tags: 'cat'}, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.id;
+ doc._id.should.eql(post._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test querying if an array contains one of multiple members $in a set': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB();
+
+ post.tags.push('football');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({tags: {$in: ['football', 'baseball']}}, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.id;
+ doc._id.should.eql(post._id);
+
+ BlogPostB.findOne({ _id: post._id, tags: /otba/i }, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.id;
+ doc._id.should.eql(post._id);
+
+ db.close();
+ })
+ });
+ });
+ },
+
+ 'test querying if an array contains one of multiple members $in a set 2': function () {
+ var db = start()
+ , BlogPostA = db.model('BlogPostB', collection)
+
+ var post = new BlogPostA({ tags: ['gooberOne'] });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ var query = {tags: {$in:[ 'gooberOne' ]}};
+
+ BlogPostA.findOne(query, function (err, returned) {
+ done();
+ should.strictEqual(err, null);
+ ;(!!~returned.tags.indexOf('gooberOne')).should.be.true;
+ returned.id;
+ returned._id.should.eql(post._id);
+ });
+ });
+
+ post.collection.insert({ meta: { visitors: 9898, a: null } }, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostA.findOne({_id: b[0]._id}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ })
+ });
+
+ var pending = 2;
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ },
+
+ 'test querying via $where a string': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({ title: 'Steve Jobs', author: 'Steve Jobs'}, function (err, created) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ $where: "this.title && this.title === this.author" }, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test querying via $where a function': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({ author: 'Atari', slug: 'Atari'}, function (err, created) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ $where: function () {
+ return (this.author && this.slug && this.author === this.slug);
+ } }, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find where $exists': function () {
+ var db = start()
+ , ExistsSchema = new Schema({
+ a: Number
+ , b: String
+ });
+ mongoose.model('Exists', ExistsSchema);
+ var Exists = db.model('Exists', 'exists_' + random());
+ Exists.create({ a: 1}, function (err, aExisting) {
+ should.strictEqual(err, null);
+ Exists.create({b: 'hi'}, function (err, bExisting) {
+ should.strictEqual(err, null);
+ Exists.find({b: {$exists: true}}, function (err, docs) {
+ should.strictEqual(err, null);
+ db.close();
+ docs.should.have.length(1);
+ });
+ });
+ });
+ },
+
+ 'test finding based on nested fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB({
+ meta: {
+ visitors: 5678
+ }
+ });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({ 'meta.visitors': 5678 }, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors')
+ .valueOf().should.equal(post.get('meta.visitors').valueOf());
+ found.id;
+ found.get('_id').should.eql(post.get('_id'));
+ db.close();
+ });
+ });
+ },
+
+ // GH-242
+ 'test finding based on embedded document fields': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({comments: [{title: 'i should be queryable'}], numbers: [1,2,33333], tags:['yes', 'no']}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({'comments.title': 'i should be queryable'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.findOne({'comments.0.title': 'i should be queryable'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ // GH-463
+ BlogPostB.findOne({'numbers.2': 33333}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.findOne({'tags.1': 'no'}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ },
+
+ // GH-389
+ 'find nested doc using string id': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({comments: [{title: 'i should be queryable by _id'}, {title:'me too me too!'}]}, function (err, created) {
+ should.strictEqual(err, null);
+ var id = created.comments[1]._id.toString();
+ BlogPostB.findOne({'comments._id': id}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(!! found, true, 'Find by nested doc id hex string fails');
+ found.id;
+ found._id.should.eql(created._id);
+ });
+ });
+ },
+
+ 'test finding where $elemMatch': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , dateAnchor = +new Date;
+
+ BlogPostB.create({comments: [{title: 'elemMatch', date: dateAnchor + 5}]}, function (err, createdAfter) {
+ should.strictEqual(err, null);
+ BlogPostB.create({comments: [{title: 'elemMatch', date: dateAnchor - 5}]}, function (err, createdBefore) {
+ should.strictEqual(err, null);
+ BlogPostB.find({'comments': {'$elemMatch': {title: 'elemMatch', date: {$gt: dateAnchor}}}},
+ function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(createdAfter._id);
+ db.close();
+ }
+ );
+ });
+ });
+ },
+
+ 'test finding where $mod': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$mod: [2, 1]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding where $not': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$not: {$mod: [2, 1]}}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test finding where $or': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+
+ Mod.create({num: 1}, {num: 2, str: 'two'}, function (err, one, two) {
+ should.strictEqual(err, null);
+
+ var pending = 3;
+ test1();
+ test2();
+ test3();
+
+ function test1 () {
+ Mod.find({$or: [{num: 1}, {num: 2}]}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(one._id);
+ found[1]._id.should.eql(two._id);
+ });
+ }
+
+ function test2 () {
+ Mod.find({ $or: [{ str: 'two'}, {str:'three'}] }, function (err, found) {
+ if (err) console.error(err);
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ });
+ }
+
+ function test3 () {
+ Mod.find({$or: [{num: 1}]}).$or([{ str: 'two' }]).run(function (err, found) {
+ if (err) console.error(err);
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(one._id);
+ found[1]._id.should.eql(two._id);
+ });
+ }
+
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ });
+ },
+
+ 'finding where #nor': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'nor_' + random());
+
+ Mod.create({num: 1}, {num: 2, str: 'two'}, function (err, one, two) {
+ should.strictEqual(err, null);
+
+ var pending = 3;
+ test1();
+ test2();
+ test3();
+
+ function test1 () {
+ Mod.find({$nor: [{num: 1}, {num: 3}]}, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(two._id);
+ });
+ }
+
+ function test2 () {
+ Mod.find({ $nor: [{ str: 'two'}, {str:'three'}] }, function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ });
+ }
+
+ function test3 () {
+ Mod.find({$nor: [{num: 2}]}).$nor([{ str: 'two' }]).run(function (err, found) {
+ done();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(one._id);
+ });
+ }
+
+ function done () {
+ if (--pending) return;
+ db.close();
+ }
+ });
+ },
+
+ 'test finding where $ne': function () {
+ var db = start()
+ , Mod = db.model('Mod', 'mods_' + random());
+ Mod.create({num: 1}, function (err, one) {
+ should.strictEqual(err, null);
+ Mod.create({num: 2}, function (err, two) {
+ should.strictEqual(err, null);
+ Mod.create({num: 3}, function (err, three) {
+ should.strictEqual(err, null);
+ Mod.find({num: {$ne: 1}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(two._id);
+ found[1]._id.should.eql(three._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding null matches null and undefined': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection + random());
+
+ BlogPostB.create(
+ { title: 'A', author: null }
+ , { title: 'B' }, function (err, createdA, createdB) {
+ should.strictEqual(err, null);
+ BlogPostB.find({author: null}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ });
+ },
+
+ 'test finding STRICT null matches': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection + random());
+
+ BlogPostB.create(
+ { title: 'A', author: null}
+ , { title: 'B' }, function (err, createdA, createdB) {
+ should.strictEqual(err, null);
+ BlogPostB.find({author: {$in: [null], $exists: true}}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(createdA._id);
+ });
+ });
+ },
+
+ 'setting a path to undefined should retain the value as undefined': function () {
+ var db = start()
+ , B = db.model('BlogPostB', collection + random())
+
+ var doc = new B;
+ doc.title='css3';
+ doc._delta().$set.title.should.equal('css3');
+ doc.title = undefined;
+ doc._delta().$unset.title.should.equal(1);
+ should.strictEqual(undefined, doc._delta().$set);
+
+ doc.title='css3';
+ doc.author = 'aaron';
+ doc.numbers = [3,4,5];
+ doc.meta.date = new Date;
+ doc.meta.visitors = 89;
+ doc.comments = [{ title: 'thanksgiving', body: 'yuuuumm' }];
+ doc.comments.push({ title: 'turkey', body: 'cranberries' });
+
+ doc.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(doc._id, function (err, b) {
+ should.strictEqual(null, err);
+ b.title.should.equal('css3');
+ b.author.should.equal('aaron');
+ should.equal(b.meta.date.toString(), doc.meta.date.toString());
+ b.meta.visitors.valueOf().should.equal(doc.meta.visitors.valueOf());
+ b.comments.length.should.equal(2);
+ b.comments[0].title.should.equal('thanksgiving');
+ b.comments[0].body.should.equal('yuuuumm');
+ b.comments[1].title.should.equal('turkey');
+ b.comments[1].body.should.equal('cranberries');
+ b.title = undefined;
+ b.author = null;
+ b.meta.date = undefined;
+ b.meta.visitors = null;
+ b.comments[0].title = null;
+ b.comments[0].body = undefined;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(b._id, function (err, b) {
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, b.title);
+ should.strictEqual(null, b.author);
+
+ should.strictEqual(undefined, b.meta.date);
+ should.strictEqual(null, b.meta.visitors);
+ should.strictEqual(null, b.comments[0].title);
+ should.strictEqual(undefined, b.comments[0].body);
+ b.comments[1].title.should.equal('turkey');
+ b.comments[1].body.should.equal('cranberries');
+
+ b.meta = undefined;
+ b.comments = undefined;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.collection.findOne({ _id: b._id}, function (err, b) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(undefined, b.meta);
+ should.strictEqual(undefined, b.comments);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding strings via regular expressions': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'Next to Normal'}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({title: /^Next/}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ var reg = '^Next to Normal$';
+
+ BlogPostB.find({ title: { $regex: reg }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(1);
+ found[0].id;
+ found[0]._id.should.eql(created._id);
+
+ BlogPostB.findOne({ title: { $regex: reg }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.where('title').$regex(reg).findOne(function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+
+ BlogPostB.where('title').$regex(/^Next/).findOne(function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding a document whose arrays contain at least $all values': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create(
+ {numbers: [-1,-2,-3,-4], meta: { visitors: 4 }}
+ , {numbers: [0,-1,-2,-3,-4]}
+ , function (err, whereoutZero, whereZero) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({numbers: {$all: [-1, -2, -3, -4]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ BlogPostB.find({'meta.visitors': {$all: [4] }}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(whereoutZero._id);
+ BlogPostB.find({numbers: {$all: [0, -1]}}, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(whereZero._id);
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding a document whose arrays contain at least $all string values': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ var post = new BlogPostB({ title: "Aristocats" });
+
+ post.tags.push('onex');
+ post.tags.push('twox');
+ post.tags.push('threex');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findById(post._id, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPostB.find({ title: { '$all': ['Aristocats']}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({ title: { '$all': [/^Aristocats/]}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({tags: { '$all': ['onex','twox','threex']}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.find({tags: { '$all': [/^onex/i]}}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+
+ BlogPostB.findOne({tags: { '$all': /^two/ }}, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.id.should.eql(post.id);
+ });
+ });
+ });
+ });
+ });
+ });
+
+ });
+ },
+
+ 'find using #all with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection + '_nestedElemMatch');
+
+ var post = new P({ title: "nested elemMatch" });
+ post.comments.push({ title: 'comment A' }, { title: 'comment B' }, { title: 'comment C' })
+
+ var id0 = post.comments[0]._id;
+ var id1 = post.comments[1]._id;
+ var id2 = post.comments[2]._id;
+
+ post.save(function (err) {
+ should.strictEqual(null, err);
+
+ var query0 = { $elemMatch: { _id: id1, title: 'comment B' }};
+ var query1 = { $elemMatch: { _id: id2.toString(), title: 'comment C' }};
+
+ P.findOne({ comments: { $all: [query0, query1] }}, function (err, p) {
+ db.close();
+ should.strictEqual(null, err);
+ p.id.should.equal(post.id);
+ });
+ });
+ },
+
+ 'find using #or with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection);
+
+ var post = new P({ title: "nested elemMatch" });
+ post.comments.push({ title: 'comment D' }, { title: 'comment E' }, { title: 'comment F' })
+
+ var id0 = post.comments[0]._id;
+ var id1 = post.comments[1]._id;
+ var id2 = post.comments[2]._id;
+
+ post.save(function (err) {
+ should.strictEqual(null, err);
+
+ var query0 = { comments: { $elemMatch: { title: 'comment Z' }}};
+ var query1 = { comments: { $elemMatch: { _id: id1.toString(), title: 'comment E' }}};
+
+ P.findOne({ $or: [query0, query1] }, function (err, p) {
+ db.close();
+ should.strictEqual(null, err);
+ p.id.should.equal(post.id);
+ });
+ });
+ },
+
+ 'find using #nor with nested #elemMatch': function () {
+ var db = start()
+ , P = db.model('BlogPostB', collection + '_norWithNestedElemMatch');
+
+ var p0 = { title: "nested $nor elemMatch1", comments: [] };
+
+ var p1 = { title: "nested $nor elemMatch0", comments: [] };
+ p1.comments.push({ title: 'comment X' }, { title: 'comment Y' }, { title: 'comment W' })
+
+ P.create(p0, p1, function (err, post0, post1) {
+ should.strictEqual(null, err);
+
+ var id = post1.comments[1]._id;
+
+ var query0 = { comments: { $elemMatch: { title: 'comment Z' }}};
+ var query1 = { comments: { $elemMatch: { _id: id.toString(), title: 'comment Y' }}};
+
+ P.find({ $nor: [query0, query1] }, function (err, posts) {
+ db.close();
+ should.strictEqual(null, err);
+ posts.length.should.equal(1);
+ posts[0].id.should.equal(post0.id);
+ });
+ });
+
+ },
+
+ 'test finding documents where an array of a certain $size': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({numbers: [1,2,3,4,5,6,7,8,9,10]}, function (err, whereoutZero) {
+ should.strictEqual(err, null);
+ BlogPostB.create({numbers: [11,12,13,14,15,16,17,18,19,20]}, function (err, whereZero) {
+ should.strictEqual(err, null);
+ BlogPostB.create({numbers: [1,2,3,4,5,6,7,8,9,10,11]}, function (err, found) {
+ BlogPostB.find({numbers: {$size: 10}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ BlogPostB.find({numbers: {$size: 11}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding documents where an array where the $slice operator': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({numbers: [500,600,700,800]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findById(created._id, {numbers: {$slice: 2}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(500);
+ found.numbers[1].should.equal(600);
+ BlogPostB.findById(created._id, {numbers: {$slice: -2}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(700);
+ found.numbers[1].should.equal(800);
+ BlogPostB.findById(created._id, {numbers: {$slice: [1, 2]}}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ found.numbers.should.have.length(2);
+ found.numbers[0].should.equal(600);
+ found.numbers[1].should.equal(700);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test finding documents with a specifc Buffer in their array': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({sigs: [new Buffer([1, 2, 3]),
+ new Buffer([4, 5, 6]),
+ new Buffer([7, 8, 9])]}, function (err, created) {
+ should.strictEqual(err, null);
+ BlogPostB.findOne({sigs: new Buffer([1, 2, 3])}, function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ var query = { sigs: { "$in" : [new Buffer([3, 3, 3]), new Buffer([4, 5, 6])] } };
+ BlogPostB.findOne(query, function (err, found) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test limits': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'first limit'}, function (err, first) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'second limit'}, function (err, second) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'third limit'}, function (err, third) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: /limit$/}).limit(2).find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(first._id);
+ found[1]._id.should.eql(second._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test skips': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: 'first skip'}, function (err, first) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'second skip'}, function (err, second) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: 'third skip'}, function (err, third) {
+ should.strictEqual(err, null);
+ BlogPostB.find({title: /skip$/}).skip(1).limit(2).find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ found[0]._id.should.eql(second._id);
+ found[1]._id.should.eql(third._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test sorts': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({meta: {visitors: 100}}, function (err, least) {
+ should.strictEqual(err, null);
+ BlogPostB.create({meta: {visitors: 300}}, function (err, largest) {
+ should.strictEqual(err, null);
+ BlogPostB.create({meta: {visitors: 200}}, function (err, middle) {
+ should.strictEqual(err, null);
+ BlogPostB
+ .where('meta.visitors').gt(99).lt(301)
+ .sort('meta.visitors', -1)
+ .find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(3);
+ found[0]._id.should.eql(largest._id);
+ found[1]._id.should.eql(middle._id);
+ found[2]._id.should.eql(least._id);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test backwards compatibility with previously existing null values in db': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB();
+
+ post.collection.insert({ meta: { visitors: 9898, a: null } }, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({_id: b[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ db.close();
+ })
+ })
+ },
+
+ 'test backwards compatibility with unused values in db': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection)
+ , post = new BlogPostB();
+
+ post.collection.insert({ meta: { visitors: 9898, color: 'blue'}}, {}, function (err, b) {
+ should.strictEqual(err, null);
+
+ BlogPostB.findOne({_id: b[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.get('meta.visitors').valueOf().should.eql(9898);
+ found.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+ })
+ })
+ },
+
+ 'test streaming cursors with #each': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: "The Wrestler", tags: ["movie"]}, function (err, wrestler) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Black Swan", tags: ["movie"]}, function (err, blackswan) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Pi", tags: ["movie"]}, function (err, pi) {
+ should.strictEqual(err, null);
+ var found = {};
+ BlogPostB
+ .find({tags: "movie"})
+ .sort('title', -1)
+ .each(function (err, post) {
+ should.strictEqual(err, null);
+ if (post) found[post.title] = 1;
+ else {
+ found.should.have.property("The Wrestler", 1);
+ found.should.have.property("Black Swan", 1);
+ found.should.have.property("Pi", 1);
+ db.close();
+ }
+ });
+ });
+ });
+ });
+ },
+
+ 'test streaming cursors with #each and manual iteration': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.create({title: "Bleu", tags: ["Krzysztof Kieślowski"]}, function (err, wrestler) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Blanc", tags: ["Krzysztof Kieślowski"]}, function (err, blackswan) {
+ should.strictEqual(err, null);
+ BlogPostB.create({title: "Rouge", tags: ["Krzysztof Kieślowski"]}, function (err, pi) {
+ should.strictEqual(err, null);
+ var found = {};
+ BlogPostB
+ .find({tags: "Krzysztof Kieślowski"})
+ .each(function (err, post, next) {
+ should.strictEqual(err, null);
+ if (post) {
+ found[post.title] = 1;
+ process.nextTick(next);
+ } else {
+ found.should.have.property("Bleu", 1);
+ found.should.have.property("Blanc", 1);
+ found.should.have.property("Rouge", 1);
+ db.close();
+ }
+ });
+ });
+ });
+ });
+ },
+
+ '$gt, $lt, $lte, $gte work on strings': function () {
+ var db = start()
+ var D = db.model('D', new Schema({dt: String}), collection);
+
+ D.create({ dt: '2011-03-30' }, done);
+ D.create({ dt: '2011-03-31' }, done);
+ D.create({ dt: '2011-04-01' }, done);
+ D.create({ dt: '2011-04-02' }, done);
+
+ var pending = 3;
+ function done (err) {
+ if (err) db.close();
+ should.strictEqual(err, null);
+
+ if (--pending) return;
+
+ pending = 2;
+
+ D.find({ 'dt': { $gte: '2011-03-30', $lte: '2011-04-01' }}).sort('dt', 1).run(function (err, docs) {
+ if (--pending) db.close();
+ should.strictEqual(err, null);
+ docs.length.should.eql(3);
+ docs[0].dt.should.eql('2011-03-30');
+ docs[1].dt.should.eql('2011-03-31');
+ docs[2].dt.should.eql('2011-04-01');
+ docs.some(function (d) { return '2011-04-02' === d.dt }).should.be.false;
+ });
+
+ D.find({ 'dt': { $gt: '2011-03-30', $lt: '2011-04-02' }}).sort('dt', 1).run(function (err, docs) {
+ if (--pending) db.close();
+ should.strictEqual(err, null);
+ docs.length.should.eql(2);
+ docs[0].dt.should.eql('2011-03-31');
+ docs[1].dt.should.eql('2011-04-01');
+ docs.some(function (d) { return '2011-03-30' === d.dt }).should.be.false;
+ docs.some(function (d) { return '2011-04-02' === d.dt }).should.be.false;
+ });
+ }
+ },
+
+ 'nested mixed queries (x.y.z)': function () {
+ var db = start()
+ , BlogPostB = db.model('BlogPostB', collection);
+
+ BlogPostB.find({ 'mixed.nested.stuff': 'skynet' }, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ },
+
+ // GH-336
+ 'finding by Date field works': function () {
+ var db = start()
+ , Test = db.model('TestDateQuery', new Schema({ date: Date }), 'datetest_' + random())
+ , now = new Date;
+
+ Test.create({ date: now }, { date: new Date(now-10000) }, function (err, a, b) {
+ should.strictEqual(err, null);
+ Test.find({ date: now }, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ });
+ });
+ },
+
+ // GH-309
+ 'using $near with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo1', geoSchema, 'geospatial'+random());
+
+ Test.create({ loc: [ 10, 20 ]}, { loc: [ 40, 90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { $near: [30, 40] }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(2);
+ });
+ }, 700);
+ });
+ },
+
+ // GH-586
+ 'using $within with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo2', geoSchema, collection + 'geospatial');
+
+ Test.create({ loc: [ 35, 50 ]}, { loc: [ -40, -90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { '$within': { '$box': [[30,40], [40,60]] }}}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ });
+ }, 700);
+ });
+ },
+
+ // GH-610
+ 'using nearSphere with Arrays works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo3', geoSchema, "y"+random());
+
+ Test.create({ loc: [ 10, 20 ]}, { loc: [ 40, 90 ]}, function (err) {
+ should.strictEqual(err, null);
+ setTimeout(function () {
+ Test.find({ loc: { $nearSphere: [30, 40] }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(2);
+ });
+ }, 700);
+ });
+ },
+
+ 'using $maxDistance with Array works (geo-spatial)': function () {
+ var db = start()
+ , Test = db.model('Geo4', geoSchema, "x"+random());
+
+ Test.create({ loc: [ 20, 80 ]}, { loc: [ 25, 30 ]}, function (err, docs) {
+ should.strictEqual(!!err, false);
+ setTimeout(function () {
+ Test.find({ loc: { $near: [25, 31], $maxDistance: 1 }}, function (err, docs) {
+ should.strictEqual(err, null);
+ docs.length.should.equal(1);
+ Test.find({ loc: { $near: [25, 32], $maxDistance: 1 }}, function (err, docs) {
+ db.close();
+ should.strictEqual(err, null);
+ docs.length.should.equal(0);
+ });
+ });
+ }, 500);
+ });
+ },
+
+ '$type tests': function () {
+ var db = start()
+ , B = db.model('BlogPostB', collection);
+
+ B.find({ title: { $type: "asd" }}, function (err, posts) {
+ err.message.should.eql("$type parameter must be Number");
+
+ B.find({ title: { $type: 2 }}, function (err, posts) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(Array.isArray(posts), true);
+ });
+ });
+ },
+
+ 'buffers find using available types': function () {
+ var db = start()
+ , BufSchema = new Schema({ name: String, block: Buffer })
+ , Test = db.model('Buffer', BufSchema, "buffers");
+
+ var docA = { name: 'A', block: new Buffer('über') };
+ var docB = { name: 'B', block: new Buffer("buffer shtuffs are neat") };
+ var docC = { name: 'C', block: 'hello world' };
+
+ Test.create(docA, docB, docC, function (err, a, b, c) {
+ should.strictEqual(err, null);
+ b.block.toString('utf8').should.equal('buffer shtuffs are neat');
+ a.block.toString('utf8').should.equal('über');
+ c.block.toString('utf8').should.equal('hello world');
+
+ Test.findById(a._id, function (err, a) {
+ should.strictEqual(err, null);
+ a.block.toString('utf8').should.equal('über');
+
+ Test.findOne({ block: 'buffer shtuffs are neat' }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('buffer shtuffs are neat');
+
+ Test.findOne({ block: /buffer/i }, function (err, rb) {
+ err.message.should.eql('Cast to buffer failed for value "/buffer/i"')
+ Test.findOne({ block: [195, 188, 98, 101, 114] }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('über');
+
+ Test.findOne({ block: 'aGVsbG8gd29ybGQ=' }, function (err, rb) {
+ should.strictEqual(err, null);
+ should.strictEqual(rb, null);
+
+ Test.findOne({ block: new Buffer('aGVsbG8gd29ybGQ=', 'base64') }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('hello world');
+
+ Test.findOne({ block: new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64') }, function (err, rb) {
+ should.strictEqual(err, null);
+ rb.block.toString('utf8').should.equal('hello world');
+
+ Test.remove({}, function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ });
+ },
+
+ 'buffer tests using conditionals': function () {
+ // $in $nin etc
+ var db = start()
+ , BufSchema = new Schema({ name: String, block: Buffer })
+ , Test = db.model('Buffer2', BufSchema, "buffer_"+random());
+
+ var docA = { name: 'A', block: new MongooseBuffer([195, 188, 98, 101, 114]) }; //über
+ var docB = { name: 'B', block: new MongooseBuffer("buffer shtuffs are neat") };
+ var docC = { name: 'C', block: new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64') };
+
+ Test.create(docA, docB, docC, function (err, a, b, c) {
+ should.strictEqual(err, null);
+ a.block.toString('utf8').should.equal('über');
+ b.block.toString('utf8').should.equal('buffer shtuffs are neat');
+ c.block.toString('utf8').should.equal('hello world');
+
+ Test.find({ block: { $in: [[195, 188, 98, 101, 114], "buffer shtuffs are neat", new Buffer('aGVsbG8gd29ybGQ=', 'base64')] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ Test.find({ block: { $in: ['über', 'hello world'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $in: ['über'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(1);
+ tests[0].block.toString('utf8').should.equal('über');
+ });
+
+ Test.find({ block: { $nin: ['über'] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $nin: [[195, 188, 98, 101, 114], new Buffer('aGVsbG8gd29ybGQ=', 'base64')] }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(1);
+ tests[0].block.toString('utf8').should.equal('buffer shtuffs are neat');
+ });
+
+ Test.find({ block: { $ne: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $gt: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ });
+
+ Test.find({ block: { $gte: 'über' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ Test.find({ block: { $lt: new Buffer('buffer shtuffs are neat') }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(2);
+ tests[0].block.toString('utf8').should.equal('über');
+ });
+
+ Test.find({ block: { $lte: 'buffer shtuffs are neat' }}, function (err, tests) {
+ done();
+ should.strictEqual(err, null);
+ tests.length.should.equal(3);
+ });
+
+ var pending = 9;
+ function done () {
+ if (--pending) return;
+ Test.remove({}, function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ }
+ });
+ },
+
+ // gh-591
+ 'querying Mixed types with elemMatch': function () {
+ var db = start()
+ , S = new Schema({ a: [{}], b: Number })
+ , M = db.model('QueryingMixedArrays', S, random())
+
+ var m = new M;
+ m.a = [1,2,{ name: 'Frodo' },'IDK', {name: 100}];
+ m.b = 10;
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.find({ a: { name: 'Frodo' }, b: '10' }, function (err, docs) {
+ should.strictEqual(null, err);
+ docs[0].a.length.should.equal(5);
+ docs[0].b.valueOf().should.equal(10);
+
+ var query = {
+ a: {
+ $elemMatch: { name: 100 }
+ }
+ }
+
+ M.find(query, function (err, docs) {
+ db.close();
+ should.strictEqual(null, err);
+ docs[0].a.length.should.equal(5);
+ });
+ });
+ });
+ },
+
+ // gh-599
+ 'regex with Array should work': function () {
+ var db = start()
+ , B = db.model('BlogPostB', random())
+
+ B.create({ tags: 'wooof baaaark meeeeow'.split(' ') }, function (err, b) {
+ should.strictEqual(null, err);
+ B.findOne({ tags: /ooof$/ }, function (err, doc) {
+ should.strictEqual(null, err);
+ should.strictEqual(true, !! doc);
+ ;(!! ~doc.tags.indexOf('meeeeow')).should.be.true;
+
+ B.findOne({ tags: {$regex: 'eow$' } }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(true, !! doc);
+ ;(!! ~doc.tags.indexOf('meeeeow')).should.be.true;
+ });
+ });
+ });
+ },
+
+ // gh-640
+ 'updating a number to null': function () {
+ var db = start()
+ var B = db.model('BlogPostB')
+ var b = new B({ meta: { visitors: null }});
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.findById(b, function (err, b) {
+ should.strictEqual(null, err);
+ should.strictEqual(b.meta.visitors, null);
+
+ B.update({ _id: b._id }, { meta: { visitors: null }}, function (err, docs) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // gh-690
+ 'using $all with ObjectIds': function () {
+ var db = start()
+
+ var SSchema = new Schema({ name: String });
+ var PSchema = new Schema({ sub: [SSchema] });
+
+ var P = db.model('usingAllWithObjectIds', PSchema);
+ var sub = [{ name: 'one' }, { name: 'two' }, { name: 'three' }];
+
+ P.create({ sub: sub }, function (err, p) {
+ should.strictEqual(null, err);
+
+ var o0 = p.sub[0]._id;
+ var o1 = p.sub[1]._id;
+ var o2 = p.sub[2]._id;
+
+ P.findOne({ 'sub._id': { $all: [o1, o2] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+
+ P.findOne({ 'sub._id': { $all: [o0, new DocumentObjectId] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ should.equal(false, !!doc);
+
+ P.findOne({ 'sub._id': { $all: [o2] }}, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+ });
+ });
+ });
+ });
+ },
+
+ 'using $all with Dates': function () {
+ var db = start()
+
+ var SSchema = new Schema({ d: Date });
+ var PSchema = new Schema({ sub: [SSchema] });
+
+ var P = db.model('usingAllWithDates', PSchema);
+ var sub = [
+ { d: new Date }
+ , { d: new Date(Date.now()-10000) }
+ , { d: new Date(Date.now()-30000) }
+ ];
+
+ P.create({ sub: sub }, function (err, p) {
+ should.strictEqual(null, err);
+
+ var o0 = p.sub[0].d;
+ var o1 = p.sub[1].d;
+ var o2 = p.sub[2].d;
+
+ P.findOne({ 'sub.d': { $all: [o1, o2] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+
+ P.findOne({ 'sub.d': { $all: [o0, new Date] }}, function (err, doc) {
+ should.strictEqual(null, err);
+ should.equal(false, !!doc);
+
+ P.findOne({ 'sub.d': { $all: [o2] }}, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(p.id);
+ });
+ });
+ });
+ });
+ },
+
+ 'excluding paths by schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false }
+ });
+
+ var S = db.model('ExcludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the excluded' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the excluded');
+ S.findById(s, function (err, s) {
+ db.close();
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ should.strictEqual(undefined, s.name);
+ });
+ });
+ },
+
+ 'excluding paths through schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false}
+ });
+
+ var S = db.model('ExcludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the excluded' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the excluded');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ should.strictEqual(undefined, s.name);
+ }
+
+ S.findById(s).exclude('thin').exec(done);
+ S.find({ _id: s._id }).select('thin').exec(done);
+ });
+ },
+
+ 'including paths through schematype': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ });
+
+ var S = db.model('IncludingBySchemaType', schema);
+ S.create({ thin: true, name: 'the included' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the included');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.true;
+ s.name.should.equal('the included');
+ }
+
+ S.findById(s).exclude('thin').exec(done);
+ S.find({ _id: s._id }).select('thin').exec(done);
+ });
+ },
+
+ 'overriding schematype select options': function () {
+ var db =start()
+
+ var selected = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ });
+ var excluded = new Schema({
+ thin: Boolean
+ , name: { type: String, select: false }
+ });
+
+ var S = db.model('OverriddingSelectedBySchemaType', selected);
+ var E = db.model('OverriddingExcludedBySchemaType', excluded);
+
+ var pending = 4;
+
+ S.create({ thin: true, name: 'the included' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('the included');
+
+ S.find({ _id: s._id }).select('thin name').exec(function (err, s) {
+ --pending || db.close();
+ s = s[0];
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.true;
+ s.isSelected('thin').should.be.true;
+ s.name.should.equal('the included');
+ s.thin.should.be.true;
+ });
+
+ S.findById(s).exclude('name').exec(function (err, s) {
+ --pending || db.close();
+ should.strictEqual(null, err);
+ s.isSelected('name').should.be.false;
+ s.isSelected('thin').should.be.true;
+ should.equal(undefined, s.name);
+ should.equal(true, s.thin);
+ })
+ });
+
+ E.create({ thin: true, name: 'the excluded' },function (err, e) {
+ should.strictEqual(null, err);
+ e.name.should.equal('the excluded');
+
+ E.find({ _id: e._id }).select('thin name').exec(function (err, e) {
+ --pending || db.close();
+ e = e[0];
+ should.strictEqual(null, err);
+ e.isSelected('name').should.be.true;
+ e.isSelected('thin').should.be.true;
+ e.name.should.equal('the excluded');
+ e.thin.should.be.true;
+ });
+
+ E.findById(e).exclude('name').exec(function (err, e) {
+ --pending || db.close();
+ should.strictEqual(null, err);
+ e.isSelected('name').should.be.false;
+ e.isSelected('thin').should.be.true;
+ should.equal(undefined, e.name);
+ should.equal(true, e.thin);
+ })
+ });
+ },
+
+ 'conflicting schematype path selection should error': function () {
+ var db =start()
+
+ var schema = new Schema({
+ thin: Boolean
+ , name: { type: String, select: true }
+ , conflict: { type: String, select: false}
+ });
+
+ var S = db.model('ConflictingBySchemaType', schema);
+ S.create({ thin: true, name: 'bing', conflict: 'crosby' },function (err, s) {
+ should.strictEqual(null, err);
+ s.name.should.equal('bing');
+ s.conflict.should.equal('crosby');
+
+ var pending = 2;
+ function done (err, s) {
+ --pending || db.close();
+ if (Array.isArray(s)) s = s[0];
+ should.equal(true, !!err);
+ }
+
+ S.findById(s).exec(done);
+ S.find({ _id: s._id }).exec(done);
+ });
+ },
+};
diff --git a/node_modules/mongoose/test/model.ref.test.js b/node_modules/mongoose/test/model.ref.test.js
new file mode 100644
index 0000000..967b6e4
--- /dev/null
+++ b/node_modules/mongoose/test/model.ref.test.js
@@ -0,0 +1,1528 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId
+ , DocObjectId = mongoose.Types.ObjectId
+
+/**
+ * Setup.
+ */
+
+/**
+ * User schema.
+ */
+
+var User = new Schema({
+ name : String
+ , email : String
+ , gender : { type: String, enum: ['male', 'female'], default: 'male' }
+ , age : { type: Number, default: 21 }
+ , blogposts : [{ type: ObjectId, ref: 'RefBlogPost' }]
+});
+
+/**
+ * Comment subdocument schema.
+ */
+
+var Comment = new Schema({
+ _creator : { type: ObjectId, ref: 'RefUser' }
+ , content : String
+});
+
+/**
+ * Blog post schema.
+ */
+
+var BlogPost = new Schema({
+ _creator : { type: ObjectId, ref: 'RefUser' }
+ , title : String
+ , comments : [Comment]
+ , fans : [{ type: ObjectId, ref: 'RefUser' }]
+});
+
+var posts = 'blogposts_' + random()
+ , users = 'users_' + random();
+
+mongoose.model('RefBlogPost', BlogPost);
+mongoose.model('RefUser', User);
+
+/**
+ * Tests.
+ */
+
+module.exports = {
+
+ 'test populating a single reference': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+ post._creator.email.should.equal('rauchg@gmail.com');
+ });
+ });
+ });
+ },
+
+ 'test an error in single reference population propagates': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts + '1')
+ , User = db.model('RefUser', users + '1');
+
+ User.create({
+ name: 'Guillermo'
+ , email: 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ var origFind = User.findOne;
+
+ // mock an error
+ User.findOne = function () {
+ var args = Array.prototype.map.call(arguments, function (arg) {
+ return 'function' == typeof arg ? function () {
+ arg(new Error('woot'));
+ } : arg;
+ });
+ return origFind.apply(this, args);
+ };
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.equal('woot');
+ });
+ });
+ });
+ },
+
+ 'test populating with partial fields selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', ['email'])
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.isInit('name').should.be.false;
+ post._creator.email.should.equal('rauchg@gmail.com');
+ });
+ });
+ });
+ },
+
+ 'populating single oid with partial field selection and filter': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Banana'
+ , email : 'cats@example.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', 'email', { name: 'Peanut' })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+ should.strictEqual(post._creator, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', 'email', { name: 'Banana' })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post._creator.should.be.an.instanceof(User);
+ post._creator.isInit('name').should.be.false;
+ post._creator.email.should.equal('cats@example.com');
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating and changing a reference': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+ post._creator.email.should.equal('rauchg@gmail.com');
+
+ User.create({
+ name : 'Aaron'
+ , email : 'aaron@learnboost.com'
+ }, function (err, newCreator) {
+ should.strictEqual(err, null);
+
+ post._creator = newCreator._id;
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('Aaron');
+ post._creator.email.should.equal('aaron@learnboost.com');
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating with partial fields selection and changing ref': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Guillermo'
+ , email : 'rauchg@gmail.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', {'name': 1})
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post._creator.should.be.an.instanceof(User);
+ post._creator.name.should.equal('Guillermo');
+
+ User.create({
+ name : 'Aaron'
+ , email : 'aaron@learnboost.com'
+ }, function (err, newCreator) {
+ should.strictEqual(err, null);
+
+ post._creator = newCreator._id;
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator', {email: 0})
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('Aaron');
+ should.not.exist(post._creator.email);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and fetching many': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans')
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].email.should.equal('fan1@learnboost.com');
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].email.should.equal('fan2@learnboost.com');
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].email.should.equal('fan1@learnboost.com');
+
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test an error in array reference population propagates': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts + '2')
+ , User = db.model('RefUser', users + '2');
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ // mock an error
+ var origFind = User.find;
+ User.find = function () {
+ var args = Array.prototype.map.call(arguments, function (arg) {
+ return 'function' == typeof arg ? function () {
+ arg(new Error('woot 2'));
+ } : arg;
+ });
+ return origFind.apply(this, args);
+ };
+
+ BlogPost
+ .find({ $or: [{ _id: post1._id }, { _id: post2._id }] })
+ .populate('fans')
+ .run(function (err, blogposts) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.equal('woot 2');
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references with fields selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 'name')
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].isInit('email').should.be.false;
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].isInit('email').should.be.false;
+ should.strictEqual(blogposts[0].fans[1].email, undefined);
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].isInit('email').should.be.false;
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].isInit('email').should.be.false;
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and filtering': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', '', { gender: 'female', _id: { $in: [fan2] }})
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(1);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+
+ blogposts[1].fans.length.should.equal(1);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].email.should.equal('fan2@learnboost.com');
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', false, { gender: 'female' })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.strictEqual(blogposts[0].fans.length, 2);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[0].fans[1].gender.should.equal('female');
+ blogposts[0].fans[1].name.should.equal('Fan 3');
+ blogposts[0].fans[1].email.should.equal('fan3@learnboost.com');
+
+ should.strictEqual(blogposts[1].fans.length, 2);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[1].fans[1].gender.should.equal('female');
+ blogposts[1].fans[1].name.should.equal('Fan 2');
+ blogposts[1].fans[1].email.should.equal('fan2@learnboost.com');
+ });
+
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and multi-filtering': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ , age : 25
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', undefined, { _id: fan3 })
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(1);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 3');
+ blogposts[0].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[0].fans[0].age, 25);
+
+ blogposts[1].fans.length.should.equal(1);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[1].fans[0].age, 25);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 0, { gender: 'female' })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ blogposts[0].fans.length.should.equal(2);
+ blogposts[0].fans[0].gender.should.equal('female');
+ blogposts[0].fans[0].name.should.equal('Fan 2');
+ blogposts[0].fans[0].email.should.equal('fan2@learnboost.com');
+ blogposts[0].fans[1].gender.should.equal('female');
+ blogposts[0].fans[1].name.should.equal('Fan 3');
+ blogposts[0].fans[1].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[0].fans[1].age, 25);
+
+ blogposts[1].fans.length.should.equal(2);
+ blogposts[1].fans[0].gender.should.equal('female');
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ should.equal(blogposts[1].fans[0].age, 25);
+ blogposts[1].fans[1].gender.should.equal('female');
+ blogposts[1].fans[1].name.should.equal('Fan 2');
+ blogposts[1].fans[1].email.should.equal('fan2@learnboost.com');
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of references and multi-filtering with field selection': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, function (err, fan1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan2) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ , gender : 'female'
+ , age : 25
+ }, function (err, fan3) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2, fan3]
+ }, function (err, post1) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan3, fan2, fan1]
+ }, function (err, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', 'name email', { gender: 'female', age: 25 })
+ .run(function (err, blogposts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.strictEqual(blogposts[0].fans.length, 1);
+ blogposts[0].fans[0].name.should.equal('Fan 3');
+ blogposts[0].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[0].fans[0].isInit('email').should.be.true;
+ blogposts[0].fans[0].isInit('gender').should.be.false;
+ blogposts[0].fans[0].isInit('age').should.be.false;
+
+ should.strictEqual(blogposts[1].fans.length, 1);
+ blogposts[1].fans[0].name.should.equal('Fan 3');
+ blogposts[1].fans[0].email.should.equal('fan3@learnboost.com');
+ blogposts[1].fans[0].isInit('email').should.be.true;
+ blogposts[1].fans[0].isInit('gender').should.be.false;
+ blogposts[1].fans[0].isInit('age').should.be.false;
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating an array of refs, changing one, and removing one': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ }, {
+ name : 'Fan 3'
+ , email : 'fan3@learnboost.com'
+ }, {
+ name : 'Fan 4'
+ , email : 'fan4@learnboost.com'
+ }, function (err, fan1, fan2, fan3, fan4) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Woot'
+ , fans : [fan1, fan2]
+ }, {
+ title : 'Woot'
+ , fans : [fan2, fan1]
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .find({ _id: { $in: [post1._id, post2._id ] } })
+ .populate('fans', ['name'])
+ .run(function (err, blogposts) {
+ should.strictEqual(err, null);
+
+ blogposts[0].fans[0].name.should.equal('Fan 1');
+ blogposts[0].fans[0].isInit('email').should.be.false;
+ blogposts[0].fans[1].name.should.equal('Fan 2');
+ blogposts[0].fans[1].isInit('email').should.be.false;
+
+ blogposts[1].fans[0].name.should.equal('Fan 2');
+ blogposts[1].fans[0].isInit('email').should.be.false;
+ blogposts[1].fans[1].name.should.equal('Fan 1');
+ blogposts[1].fans[1].isInit('email').should.be.false;
+
+ blogposts[1].fans = [fan3, fan4];
+
+ blogposts[1].save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(blogposts[1]._id, [], { populate: ['fans'] })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans[0].name.should.equal('Fan 3');
+ post.fans[1].name.should.equal('Fan 4');
+
+ post.fans.splice(0, 1);
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('fans')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.fans.length.should.equal(1);
+ post.fans[0].name.should.equal('Fan 4');
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({ name: 'User 1' }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({ name: 'User 2' }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title: 'Woot'
+ , _creator: user1._id
+ , comments: [
+ { _creator: user1._id, content: 'Woot woot' }
+ , { _creator: user2._id, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .populate('comments._creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post._creator.name.should.equal('User 1');
+ post.comments[0]._creator.name.should.equal('User 1');
+ post.comments[1]._creator.name.should.equal('User 2');
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments partially': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'User 1'
+ , email : 'user1@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'User 2'
+ , email : 'user2@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: user1, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', ['email'])
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.comments[0]._creator.email.should.equal('user1@learnboost.com');
+ post.comments[0]._creator.isInit('name').should.be.false;
+ post.comments[1]._creator.email.should.equal('user2@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ });
+ });
+ });
+ });
+ },
+
+ 'test populating subdocuments partially with conditions': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'User 1'
+ , email : 'user1@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'User 2'
+ , email : 'user2@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: user1, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', {'email': 1}, { name: /User/ })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.comments[0]._creator.email.should.equal('user1@learnboost.com');
+ post.comments[0]._creator.isInit('name').should.be.false;
+ post.comments[1]._creator.email.should.equal('user2@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ });
+ });
+ });
+ });
+ },
+
+ 'populating subdocs with invalid/missing subproperties': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({
+ name : 'T-100'
+ , email : 'terminator100@learnboost.com'
+ }, function (err, user1) {
+ should.strictEqual(err, null);
+
+ User.create({
+ name : 'T-1000'
+ , email : 'terminator1000@learnboost.com'
+ }, function (err, user2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [
+ { _creator: null, content: 'Woot woot' }
+ , { _creator: user2, content: 'Wha wha' }
+ ]
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ // invalid subprop
+ BlogPost
+ .findById(post._id)
+ .populate('comments._idontexist', 'email')
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+ should.exist(post);
+ post.comments.length.should.equal(2);
+ should.strictEqual(post.comments[0]._creator, null);
+ post.comments[1]._creator.toString().should.equal(user2.id);
+
+ // subprop is null in a doc
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', 'email')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.exist(post);
+ post.comments.length.should.equal(2);
+ should.strictEqual(post.comments[0]._creator, null);
+ should.strictEqual(post.comments[0].content, 'Woot woot');
+ post.comments[1]._creator.email.should.equal('terminator1000@learnboost.com');
+ post.comments[1]._creator.isInit('name').should.be.false;
+ post.comments[1].content.should.equal('Wha wha');
+ });
+ });
+ });
+ });
+ });
+ },
+
+ // gh-481
+ 'test populating subdocuments partially with empty array': function (beforeExit) {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , worked = false;
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , comments: [] // EMPTY ARRAY
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('comments._creator', ['email'])
+ .run(function (err, returned) {
+ db.close();
+ worked = true;
+ should.strictEqual(err, null);
+ returned.id.should.equal(post.id);
+ });
+ });
+
+ beforeExit(function () {
+ worked.should.be.true;
+ });
+ },
+
+ 'populating subdocuments with array including nulls': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ var user = new User({ name: 'hans zimmer' });
+ user.save(function (err) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , fans: []
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ // shove some uncasted vals
+ BlogPost.collection.update({ _id: post._id }, { $set: { fans: [null, undefined, user.id, null] } }, function (err) {
+ should.strictEqual(err, undefined);
+
+ BlogPost
+ .findById(post._id)
+ .populate('fans', ['name'])
+ .run(function (err, returned) {
+ db.close();
+ should.strictEqual(err, null);
+ returned.id.should.equal(post.id);
+ returned.fans.length.should.equal(1);
+ });
+ })
+ });
+ });
+ },
+
+ 'populating more than one array at a time': function () {
+ var db = start()
+ , User = db.model('RefUser', users)
+ , M = db.model('PopMultiSubDocs', new Schema({
+ users: [{ type: ObjectId, ref: 'RefUser' }]
+ , fans: [{ type: ObjectId, ref: 'RefUser' }]
+ , comments: [Comment]
+ }))
+
+ User.create({
+ email : 'fan1@learnboost.com'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, {
+ name: 'Fan 3'
+ }, function (err, fan1, fan2, fan3) {
+ should.strictEqual(err, null);
+
+ M.create({
+ users: [fan3]
+ , fans: [fan1]
+ , comments: [
+ { _creator: fan1, content: 'bejeah!' }
+ , { _creator: fan2, content: 'chickfila' }
+ ]
+ }, {
+ users: [fan1]
+ , fans: [fan2]
+ , comments: [
+ { _creator: fan3, content: 'hello' }
+ , { _creator: fan1, content: 'world' }
+ ]
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+
+ M.where('_id').in([post1, post2])
+ .populate('fans', 'name', { gender: 'female' })
+ .populate('users', 'name', { gender: 'male' })
+ .populate('comments._creator', ['email'], { name: null })
+ .run(function (err, posts) {
+ db.close();
+ should.strictEqual(err, null);
+
+ should.exist(posts);
+ posts.length.should.equal(2);
+ var p1 = posts[0];
+ var p2 = posts[1];
+ should.strictEqual(p1.fans.length, 0);
+ should.strictEqual(p2.fans.length, 1);
+ p2.fans[0].name.should.equal('Fan 2');
+ p2.fans[0].isInit('email').should.be.false;
+ p2.fans[0].isInit('gender').should.be.false;
+ p1.comments.length.should.equal(2);
+ p2.comments.length.should.equal(2);
+ should.exist(p1.comments[0]._creator.email);
+ should.not.exist(p2.comments[0]._creator);
+ p1.comments[0]._creator.email.should.equal('fan1@learnboost.com');
+ p2.comments[1]._creator.email.should.equal('fan1@learnboost.com');
+ p1.comments[0]._creator.isInit('name').should.be.false;
+ p2.comments[1]._creator.isInit('name').should.be.false;
+ p1.comments[0].content.should.equal('bejeah!');
+ p2.comments[1].content.should.equal('world');
+ should.not.exist(p1.comments[1]._creator);
+ should.not.exist(p2.comments[0]._creator);
+ p1.comments[1].content.should.equal('chickfila');
+ p2.comments[0].content.should.equal('hello');
+ });
+ });
+ });
+ },
+
+ 'populating multiple children of a sub-array at a time': function () {
+ var db = start()
+ , User = db.model('RefUser', users)
+ , BlogPost = db.model('RefBlogPost', posts)
+ , Inner = new Schema({
+ user: { type: ObjectId, ref: 'RefUser' }
+ , post: { type: ObjectId, ref: 'RefBlogPost' }
+ })
+ , I = db.model('PopMultiChildrenOfSubDocInner', Inner)
+ var M = db.model('PopMultiChildrenOfSubDoc', new Schema({
+ kids: [Inner]
+ }))
+
+ User.create({
+ name : 'Fan 1'
+ , email : 'fan1@learnboost.com'
+ , gender : 'male'
+ }, {
+ name : 'Fan 2'
+ , email : 'fan2@learnboost.com'
+ , gender : 'female'
+ }, function (err, fan1, fan2) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'woot'
+ }, {
+ title : 'yay'
+ }, function (err, post1, post2) {
+ should.strictEqual(err, null);
+ M.create({
+ kids: [
+ { user: fan1, post: post1, y: 5 }
+ , { user: fan2, post: post2, y: 8 }
+ ]
+ , x: 4
+ }, function (err, m1) {
+ should.strictEqual(err, null);
+
+ M.findById(m1)
+ .populate('kids.user', ["name"])
+ .populate('kids.post', ["title"], { title: "woot" })
+ .run(function (err, o) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(o.kids.length, 2);
+ var k1 = o.kids[0];
+ var k2 = o.kids[1];
+ should.strictEqual(true, !k2.post);
+ should.strictEqual(k1.user.name, "Fan 1");
+ should.strictEqual(k1.user.email, undefined);
+ should.strictEqual(k1.post.title, "woot");
+ should.strictEqual(k2.user.name, "Fan 2");
+ });
+ });
+ });
+ });
+ },
+
+ 'passing sort options to the populate method': function () {
+ var db = start()
+ , P = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users);
+
+ User.create({ name: 'aaron', age: 10 }, { name: 'fan2', age: 8 }, { name: 'someone else', age: 3 },
+ function (err, fan1, fan2, fan3) {
+ should.strictEqual(err, null);
+
+ P.create({ fans: [fan2, fan3, fan1] }, function (err, post) {
+ should.strictEqual(err, null);
+
+ P.findById(post)
+ .populate('fans', null, null, { sort: 'name' })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(3);
+ post.fans[0].name.should.equal('aaron');
+ post.fans[1].name.should.equal('fan2');
+ post.fans[2].name.should.equal('someone else');
+
+ P.findById(post)
+ .populate('fans', 'name', null, { sort: [['name', -1]] })
+ .run(function (err, post) {
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(3);
+ post.fans[2].name.should.equal('aaron');
+ should.strictEqual(undefined, post.fans[2].age)
+ post.fans[1].name.should.equal('fan2');
+ should.strictEqual(undefined, post.fans[1].age)
+ post.fans[0].name.should.equal('someone else');
+ should.strictEqual(undefined, post.fans[0].age)
+
+ P.findById(post)
+ .populate('fans', 'age', { age: { $gt: 3 }}, { sort: [['name', 'desc']] })
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ post.fans.length.should.equal(2);
+ post.fans[1].age.valueOf().should.equal(10);
+ post.fans[0].age.valueOf().should.equal(8);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'refs should cast to ObjectId from hexstrings': function () {
+ var BP = mongoose.model('RefBlogPost', BlogPost);
+ var bp = new BP;
+ bp._creator = new DocObjectId().toString();
+ bp._creator.should.be.an.instanceof(DocObjectId);
+ bp.set('_creator', new DocObjectId().toString());
+ bp._creator.should.be.an.instanceof(DocObjectId);
+ },
+
+ 'populate should work on String _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: String
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: String, ref: 'UserWithStringId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithStringId', UserSchema, random())
+ var Note = db.model('NoteWithStringId', NoteSchema, random())
+
+ var alice = new User({_id: 'alice', name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 'alice', body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ },
+
+ 'populate should work on Number _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: Number
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: Number, ref: 'UserWithNumberId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithNumberId', UserSchema, random())
+ var Note = db.model('NoteWithNumberId', NoteSchema, random())
+
+ var alice = new User({_id: 2359, name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 2359, body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ },
+
+ // gh-577
+ 'required works on ref fields': function () {
+ var db = start();
+
+ var userSchema = new Schema({
+ email: {type: String, required: true}
+ });
+ var User = db.model('ObjectIdRefRequiredField', userSchema, random());
+
+ var numSchema = new Schema({ _id: Number, val: Number });
+ var Num = db.model('NumberRefRequired', numSchema, random());
+
+ var strSchema = new Schema({ _id: String, val: String });
+ var Str = db.model('StringRefRequired', strSchema, random());
+
+ var commentSchema = new Schema({
+ user: {type: ObjectId, ref: 'ObjectIdRefRequiredField', required: true}
+ , num: {type: Number, ref: 'NumberRefRequired', required: true}
+ , str: {type: String, ref: 'StringRefRequired', required: true}
+ , text: String
+ });
+ var Comment = db.model('CommentWithRequiredField', commentSchema);
+
+ var pending = 3;
+
+ var string = new Str({ _id: 'my string', val: 'hello' });
+ var number = new Num({ _id: 1995, val: 234 });
+ var user = new User({ email: 'test' });
+
+ string.save(next);
+ number.save(next);
+ user.save(next);
+
+ function next (err) {
+ should.strictEqual(null, err);
+ if (--pending) return;
+
+ var comment = new Comment({
+ text: 'test'
+ });
+
+ comment.save(function (err) {
+ should.equal('Validation failed', err && err.message);
+ err.errors.should.have.property('num');
+ err.errors.should.have.property('str');
+ err.errors.should.have.property('user');
+ err.errors.num.type.should.equal('required');
+ err.errors.str.type.should.equal('required');
+ err.errors.user.type.should.equal('required');
+
+ comment.user = user;
+ comment.num = 1995;
+ comment.str = 'my string';
+
+ comment.save(function (err, comment) {
+ should.strictEqual(null, err);
+
+ Comment
+ .findById(comment.id)
+ .populate('user')
+ .populate('num')
+ .populate('str')
+ .run(function (err, comment) {
+ should.strictEqual(err, null);
+
+ comment.set({text: 'test2'});
+
+ comment.save(function (err, comment) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ }
+ },
+
+ 'populate works with schemas with both id and _id defined': function () {
+ var db =start()
+ , S1 = new Schema({ id: String })
+ , S2 = new Schema({ things: [{ type: ObjectId, ref: '_idAndid' }]})
+
+ var M1 = db.model('_idAndid', S1);
+ var M2 = db.model('populateWorksWith_idAndidSchemas', S2);
+
+ M1.create(
+ { id: "The Tiger That Isn't" }
+ , { id: "Users Guide To The Universe" }
+ , function (err, a, b) {
+ should.strictEqual(null, err);
+
+ var m2 = new M2({ things: [a, b]});
+ m2.save(function (err) {
+ should.strictEqual(null, err);
+ M2.findById(m2).populate('things').run(function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.things.length.should.equal(2);
+ doc.things[0].id.should.equal("The Tiger That Isn't");
+ doc.things[1].id.should.equal("Users Guide To The Universe");
+ })
+ });
+ })
+ },
+
+ // gh-602
+ 'Update works with populated arrays': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ var user1 = new User({ name: 'aphex' });
+ var user2 = new User({ name: 'twin' });
+
+ User.create({name:'aphex'},{name:'twin'}, function (err, u1, u2) {
+ should.strictEqual(err, null);
+
+ var post = BlogPost.create({
+ title: 'Woot'
+ , fans: []
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ var update = { fans: [u1, u2] };
+ BlogPost.update({ _id: post }, update, function (err) {
+ should.strictEqual(err, null);
+
+ // the original update doc should not be modified
+ ;('fans' in update).should.be.true;
+ ;('$set' in update).should.be.false;
+ update.fans[0].should.be.instanceof(mongoose.Document);
+ update.fans[1].should.be.instanceof(mongoose.Document);
+
+ BlogPost.findById(post, function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.fans.length.should.equal(2);
+ post.fans[0].should.be.instanceof(DocObjectId);
+ post.fans[1].should.be.instanceof(DocObjectId);
+ });
+ });
+ });
+ });
+ },
+
+ // gh-675
+ 'toJSON should also be called for refs': function () {
+ var db = start()
+ , BlogPost = db.model('RefBlogPost', posts)
+ , User = db.model('RefUser', users)
+
+ User.prototype._toJSON = User.prototype.toJSON;
+ User.prototype.toJSON = function() {
+ var res = this._toJSON();
+ res.was_in_to_json = true;
+ return res;
+ }
+
+ BlogPost.prototype._toJSON = BlogPost.prototype.toJSON;
+ BlogPost.prototype.toJSON = function() {
+ var res = this._toJSON();
+ res.was_in_to_json = true;
+ return res;
+ }
+
+ User.create({
+ name : 'Jerem'
+ , email : 'jerem@jolicloud.com'
+ }, function (err, creator) {
+ should.strictEqual(err, null);
+
+ BlogPost.create({
+ title : 'Ping Pong'
+ , _creator : creator
+ }, function (err, post) {
+ should.strictEqual(err, null);
+
+ BlogPost
+ .findById(post._id)
+ .populate('_creator')
+ .run(function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+
+ var json = post.toJSON();
+ json.was_in_to_json.should.equal(true);
+ json._creator.was_in_to_json.should.equal(true);
+ });
+ });
+ });
+ },
+
+ // gh-686
+ 'populate should work on Buffer _ids': function () {
+ var db = start();
+
+ var UserSchema = new Schema({
+ _id: Buffer
+ , name: String
+ })
+
+ var NoteSchema = new Schema({
+ author: { type: Buffer, ref: 'UserWithBufferId' }
+ , body: String
+ })
+
+ var User = db.model('UserWithBufferId', UserSchema, random())
+ var Note = db.model('NoteWithBufferId', NoteSchema, random())
+
+ var alice = new User({_id: new mongoose.Types.Buffer('YWxpY2U=', 'base64'), name: "Alice"})
+
+ alice.save(function (err) {
+ should.strictEqual(err, null);
+
+ var note = new Note({author: 'alice', body: "Buy Milk"});
+ note.save(function (err) {
+ should.strictEqual(err, null);
+
+ Note.findById(note.id).populate('author').run(function (err, note) {
+ db.close();
+ should.strictEqual(err, null);
+ note.body.should.equal('Buy Milk');
+ should.exist(note.author);
+ note.author.name.should.equal('Alice');
+ });
+ });
+ })
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/test/model.stream.test.js b/node_modules/mongoose/test/model.stream.test.js
new file mode 100644
index 0000000..a010b0e
--- /dev/null
+++ b/node_modules/mongoose/test/model.stream.test.js
@@ -0,0 +1,232 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , utils = require('../lib/utils')
+ , random = utils.random
+ , Query = require('../lib/query')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , CastError = SchemaType.CastError
+ , ObjectId = Schema.ObjectId
+ , MongooseBuffer = mongoose.Types.Buffer
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , fs = require('fs')
+
+var names = ('Aaden Aaron Adrian Aditya Agustin Jim Bob Jonah Frank Sally Lucy').split(' ');
+
+/**
+ * Setup.
+ */
+
+var Person = new Schema({
+ name: String
+});
+
+mongoose.model('PersonForStream', Person);
+var collection = 'personforstream_' + random();
+
+;(function setup () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+
+ var people = names.map(function (name) {
+ return { name: name };
+ });
+
+ P.create(people, function (err) {
+ should.strictEqual(null, err);
+ db.close();
+ assignExports();
+ });
+})()
+
+function assignExports () { var o = {
+
+ 'cursor stream': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , i = 0
+ , closed = 0
+ , paused = 0
+ , resumed = 0
+ , err
+
+ var stream = P.find({}).stream();
+
+ stream.on('data', function (doc) {
+ should.strictEqual(true, !! doc.name);
+ should.strictEqual(true, !! doc._id);
+
+ if (paused > 0 && 0 === resumed) {
+ err = new Error('data emitted during pause');
+ return done();
+ }
+
+ if (++i === 3) {
+ stream.paused.should.be.false;
+ stream.pause();
+ stream.paused.should.be.true;
+ paused++;
+
+ setTimeout(function () {
+ stream.paused.should.be.true;
+ resumed++;
+ stream.resume();
+ stream.paused.should.be.false;
+ }, 20);
+ }
+ });
+
+ stream.on('error', function (er) {
+ err = er;
+ done();
+ });
+
+ stream.on('close', function () {
+ closed++;
+ done();
+ });
+
+ function done () {
+ db.close();
+ should.strictEqual(undefined, err);
+ should.equal(i, names.length);
+ closed.should.equal(1);
+ paused.should.equal(1);
+ resumed.should.equal(1);
+ stream._cursor.isClosed().should.be.true;
+ }
+ }
+
+, 'immediately destroying a stream prevents the query from executing': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , i = 0
+
+ var stream = P.where('name', 'Jonah').select('name').findOne().stream();
+
+ stream.on('data', function () {
+ i++;
+ })
+ stream.on('close', done);
+ stream.on('error', done);
+
+ stream.destroy();
+
+ function done (err) {
+ should.strictEqual(undefined, err);
+ i.should.equal(0);
+ process.nextTick(function () {
+ db.close();
+ should.strictEqual(null, stream._fields);
+ })
+ }
+ }
+
+, 'destroying a stream stops it': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , finished = 0
+ , i = 0
+
+ var stream = P.where('name').$exists().limit(10).only('_id').stream();
+
+ should.strictEqual(null, stream._destroyed);
+ stream.readable.should.be.true;
+
+ stream.on('data', function (doc) {
+ should.strictEqual(undefined, doc.name);
+ if (++i === 5) {
+ stream.destroy();
+ stream.readable.should.be.false;
+ }
+ });
+
+ stream.on('close', done);
+ stream.on('error', done);
+
+ function done (err) {
+ ++finished;
+ setTimeout(function () {
+ db.close();
+ should.strictEqual(undefined, err);
+ i.should.equal(5);
+ finished.should.equal(1);
+ stream._destroyed.should.equal(true);
+ stream.readable.should.be.false;
+ stream._cursor.isClosed().should.be.true;
+ }, 150)
+ }
+ }
+
+, 'cursor stream errors': function () {
+ var db = start({ server: { auto_reconnect: false }})
+ , P = db.model('PersonForStream', collection)
+ , finished = 0
+ , closed = 0
+ , i = 0
+
+ var stream = P.find().batchSize(5).stream();
+
+ stream.on('data', function (doc) {
+ if (++i === 5) {
+ db.close();
+ }
+ });
+
+ stream.on('close', function () {
+ closed++;
+ });
+
+ stream.on('error', done);
+
+ function done (err) {
+ ++finished;
+ setTimeout(function () {
+ should.equal('no open connections', err.message);
+ i.should.equal(5);
+ closed.should.equal(1);
+ finished.should.equal(1);
+ stream._destroyed.should.equal(true);
+ stream.readable.should.be.false;
+ stream._cursor.isClosed().should.be.true;
+ }, 150)
+ }
+ }
+
+, 'cursor stream pipe': function () {
+ var db = start()
+ , P = db.model('PersonForStream', collection)
+ , filename = '/tmp/_mongoose_stream_out.txt'
+ , out = fs.createWriteStream(filename)
+
+ var stream = P.find().sort('name', 1).limit(20).stream();
+ stream.pipe(out);
+
+ stream.on('error', done);
+ stream.on('close', done);
+
+ function done (err) {
+ db.close();
+ should.strictEqual(undefined, err);
+ var contents = fs.readFileSync(filename, 'utf8');
+ ;/Aaden/.test(contents).should.be.true;
+ ;/Aaron/.test(contents).should.be.true;
+ ;/Adrian/.test(contents).should.be.true;
+ ;/Aditya/.test(contents).should.be.true;
+ ;/Agustin/.test(contents).should.be.true;
+ fs.unlink(filename);
+ }
+ }
+}
+
+// end exports
+
+utils.merge(exports, o);
+
+}
diff --git a/node_modules/mongoose/test/model.test.js b/node_modules/mongoose/test/model.test.js
new file mode 100644
index 0000000..22c7433
--- /dev/null
+++ b/node_modules/mongoose/test/model.test.js
@@ -0,0 +1,4682 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , 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
+ , MongooseNumber = mongoose.Types.Number
+ , 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]
+});
+
+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();
+
+module.exports = {
+
+ 'test a model isNew flag when instantiating': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.isNew.should.be.true;
+ db.close();
+ },
+
+ 'modified getter should not throw': function () {
+ var db = start();
+ var BlogPost = db.model('BlogPost', collection);
+ var post = new BlogPost;
+ db.close();
+
+ var threw = false;
+ try {
+ post.modified;
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ 'test presence of model schema': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.schema.should.be.an.instanceof(Schema);
+ BlogPost.prototype.schema.should.be.an.instanceof(Schema);
+ db.close();
+ },
+
+ 'test a model default structure when instantiated': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.isNew.should.be.true;
+ post.db.model('BlogPost').modelName.should.equal('BlogPost');
+ post.constructor.modelName.should.equal('BlogPost');
+
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.equal(undefined, post.get('published'));
+
+ post.get('numbers').should.be.an.instanceof(MongooseArray);
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ },
+
+ 'mongoose.model returns the model at creation': function () {
+ var Named = mongoose.model('Named', new Schema({ name: String }));
+ var n1 = new Named();
+ should.equal(n1.name, null);
+ var n2 = new Named({ name: 'Peter Bjorn' });
+ should.equal(n2.name, 'Peter Bjorn');
+
+ var schema = new Schema({ number: Number });
+ var Numbered = mongoose.model('Numbered', schema, collection);
+ var n3 = new Numbered({ number: 1234 });
+ n3.number.valueOf().should.equal(1234);
+ },
+
+ 'mongoose.model!init emits init event on schema': 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();
+ model.should.equal(Named);
+ },
+
+ 'collection name can be specified through schema': function () {
+ var schema = new Schema({ name: String }, { collection: 'users1' });
+ var Named = mongoose.model('CollectionNamedInSchema1', schema);
+ Named.prototype.collection.name.should.equal('users1');
+
+ var db = start();
+ var users2schema = new Schema({ name: String }, { collection: 'users2' });
+ var Named2 = db.model('CollectionNamedInSchema2', users2schema);
+ db.close();
+ Named2.prototype.collection.name.should.equal('users2');
+ },
+
+ 'test default Array type casts to Mixed': function () {
+ var db = start()
+ , DefaultArraySchema = new Schema({
+ num1: Array
+ , num2: []
+ })
+
+ mongoose.model('DefaultArraySchema', DefaultArraySchema);
+ var DefaultArray = db.model('DefaultArraySchema', collection);
+ var arr = new DefaultArray();
+
+ arr.get('num1').should.have.length(0);
+ arr.get('num2').should.have.length(0);
+
+ var threw1 = false
+ , threw2 = false;
+
+ try {
+ arr.num1.push({ x: 1 })
+ arr.num1.push(9)
+ arr.num1.push("woah")
+ } catch (err) {
+ threw1 = true;
+ }
+
+ threw1.should.equal(false);
+
+ try {
+ arr.num2.push({ x: 1 })
+ arr.num2.push(9)
+ arr.num2.push("woah")
+ } catch (err) {
+ threw2 = true;
+ }
+
+ threw2.should.equal(false);
+
+ db.close();
+
+ },
+
+ 'test a model default structure that has a nested Array when instantiated': function () {
+ var db = start()
+ , NestedSchema = new Schema({
+ nested: {
+ array: [Number]
+ }
+ });
+ mongoose.model('NestedNumbers', NestedSchema);
+ var NestedNumbers = db.model('NestedNumbers', collection);
+
+ var nested = new NestedNumbers();
+ nested.get('nested.array').should.be.an.instanceof(MongooseArray);
+ db.close();
+ },
+
+ 'test a model that defaults an Array attribute to a default non-empty array': function () {
+ var db = start()
+ , DefaultArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: ['a', 'b', 'c']}
+ });
+ mongoose.model('DefaultArray', DefaultArraySchema);
+ var DefaultArray = db.model('DefaultArray', collection);
+ var arr = new DefaultArray();
+ arr.get('arr').should.have.length(3);
+ arr.get('arr')[0].should.equal('a');
+ arr.get('arr')[1].should.equal('b');
+ arr.get('arr')[2].should.equal('c');
+ db.close();
+ },
+
+ 'test a model that defaults an Array attribute to a default single member array': function () {
+ var db = start()
+ , DefaultOneCardArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: ['a']}
+ });
+ mongoose.model('DefaultOneCardArray', DefaultOneCardArraySchema);
+ var DefaultOneCardArray = db.model('DefaultOneCardArray', collection);
+ var arr = new DefaultOneCardArray();
+ arr.get('arr').should.have.length(1);
+ arr.get('arr')[0].should.equal('a');
+ db.close();
+ },
+
+ 'test a model that defaults an Array attributes to an empty array': function () {
+ var db = start()
+ , DefaultZeroCardArraySchema = new Schema({
+ arr: {type: Array, cast: String, default: []}
+ });
+ mongoose.model('DefaultZeroCardArray', DefaultZeroCardArraySchema);
+ var DefaultZeroCardArray = db.model('DefaultZeroCardArray', collection);
+ var arr = new DefaultZeroCardArray();
+ arr.get('arr').should.have.length(0);
+ arr.arr.should.have.length(0);
+ db.close();
+ },
+
+ 'test that arrays auto-default to the empty array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.should.have.length(0);
+ db.close();
+ },
+
+ 'test instantiating a model with a hash that maps to at least 1 null value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: null
+ });
+ should.strictEqual(null, post.title);
+ db.close();
+ },
+
+ 'saving a model with a null value should perpetuate that null value to the db': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: null
+ });
+ should.strictEqual(null, post.title);
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(found.title, null);
+ });
+ });
+ },
+
+ 'test instantiating a model with a hash that maps to at least 1 undefined value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ title: undefined
+ });
+ should.strictEqual(undefined, post.title);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(found.title, undefined);
+ });
+ });
+ },
+
+ 'test a model structure when saved': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , pending = 2;
+
+ function done () {
+ if (!--pending) db.close();
+ }
+
+ var post = new BlogPost();
+ post.on('save', function (post) {
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+ should.equal(undefined, post.get('published'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ done();
+ });
+
+ post.save(function(err, post){
+ should.strictEqual(err, null);
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.equal(undefined, post.get('title'));
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+ should.equal(undefined, post.get('published'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ done();
+ });
+ },
+
+ 'test a model structures when created': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({ title: 'hi there'}, function (err, post) {
+ should.strictEqual(err, null);
+ post.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ should.strictEqual(post.get('title'), 'hi there');
+ should.equal(undefined, post.get('slug'));
+ should.equal(undefined, post.get('date'));
+
+ post.get('meta').should.be.a('object');
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.strictEqual(undefined, post.get('published'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ });
+ },
+
+ 'test a model structure when initd': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ 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' }
+ ]
+ });
+
+ post.get('title').should.eql('Test');
+ post.get('slug').should.eql('test');
+ post.get('date').should.be.an.instanceof(Date);
+ post.get('meta').should.be.a('object');
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('published').should.be.true;
+
+ post.title.should.eql('Test');
+ post.slug.should.eql('test');
+ post.date.should.be.an.instanceof(Date);
+ post.meta.should.be.a('object');
+ post.meta.date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.published.should.be.true;
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('owners')[0].should.be.an.instanceof(DocumentObjectId);
+ post.get('owners')[1].should.be.an.instanceof(DocumentObjectId);
+
+ post.owners.should.be.an.instanceof(MongooseArray);
+ post.owners[0].should.be.an.instanceof(DocumentObjectId);
+ post.owners[1].should.be.an.instanceof(DocumentObjectId);
+
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ post.get('comments')[0].should.be.an.instanceof(EmbeddedDocument);
+ post.get('comments')[1].should.be.an.instanceof(EmbeddedDocument);
+
+ post.comments.should.be.an.instanceof(DocumentArray);
+ post.comments[0].should.be.an.instanceof(EmbeddedDocument);
+ post.comments[1].should.be.an.instanceof(EmbeddedDocument);
+
+ db.close();
+ },
+
+ 'test a model structure when partially initd': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.get('title').should.eql('Test');
+ post.get('slug').should.eql('test');
+ post.get('date').should.be.an.instanceof(Date);
+ post.get('meta').should.be.a('object');
+
+ post.get('meta').should.eql({});
+ should.equal(undefined, post.get('meta.date'));
+ should.equal(undefined, post.get('meta.visitors'));
+
+ should.equal(undefined, post.get('published'));
+
+ post.get('owners').should.be.an.instanceof(MongooseArray);
+ post.get('comments').should.be.an.instanceof(DocumentArray);
+ db.close();
+ },
+
+ 'test initializing with a nested hash': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ meta: {
+ date : new Date
+ , visitors : 5
+ }
+ });
+
+ post.get('meta.visitors').valueOf().should.equal(5);
+ db.close();
+ },
+
+ 'test isNew on embedded documents after initing': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.get('comments')[0].isNew.should.be.false;
+ db.close();
+ },
+
+ 'test isNew on embedded documents after saving': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' })
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.invalidate('title'); // force error
+ post.save(function (err) {
+ post.isNew.should.be.true;
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ post.isNew.should.be.false;
+ post.get('comments')[0].isNew.should.be.false;
+ post.get('comments')[0].comments[0].isNew.should.be.false;
+ });
+ });
+ },
+
+ 'test isNew on embedded documents added after saving': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' })
+ post.save(function (err) {
+ post.isNew.should.be.false;
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.get('comments')[0].isNew.should.be.true;
+ post.get('comments')[0].comments[0].isNew.should.be.true;
+ post.save(function (err) {
+ should.strictEqual(null, err);
+ post.isNew.should.be.false;
+ post.get('comments')[0].isNew.should.be.false;
+ post.get('comments')[0].comments[0].isNew.should.be.false;
+ db.close();
+ });
+ });
+ },
+
+ 'modified states are reset after save runs': function () {
+ var db = start()
+ , B = db.model('BlogPost', collection)
+ , pending = 2;
+
+ var b = new B;
+
+ b.numbers.push(3);
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ --pending || find();
+ });
+
+ b.numbers.push(3);
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ --pending || find();
+ });
+
+ function find () {
+ B.findById(b, function (err, b) {
+ db.close();
+ should.strictEqual(null, err);
+ b.numbers.length.should.equal(2);
+ });
+ }
+ },
+
+ 'modified states in emb-doc are reset after save runs': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ title: 'hocus pocus' });
+ post.comments.push({ title: 'Humpty Dumpty', comments: [{title: 'nested'}] });
+ post.save(function(err){
+ db.close();
+ should.strictEqual(null, err);
+ var mFlag = post.comments[0].isModified('title');
+ mFlag.should.equal(false);
+ post.isModified('title').should.equal(false);
+ });
+
+ },
+
+ 'test isModified when modifying keys': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.isModified('title').should.be.false;
+ post.set('title', 'test');
+ post.isModified('title').should.be.true;
+
+ post.isModified('date').should.be.false;
+ post.set('date', new Date(post.date + 1));
+ post.isModified('date').should.be.true;
+
+ post.isModified('meta.date').should.be.false;
+ db.close();
+ },
+
+ 'setting a key identically to its current value should not dirty the key': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ });
+
+ post.isModified('title').should.be.false;
+ post.set('title', 'Test');
+ post.isModified('title').should.be.false;
+ db.close();
+ },
+
+ 'test isModified and isDirectModified on a DocumentArray': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.isModified('comments.0.title').should.be.false;
+ post.get('comments')[0].set('title', 'Woot');
+ post.isModified('comments').should.be.true;
+ post.isDirectModified('comments').should.be.false;
+ post.isModified('comments.0.title').should.be.true;
+ post.isDirectModified('comments.0.title').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a DocumentArray with accessors': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+ post.init({
+ title : 'Test'
+ , slug : 'test'
+ , comments : [ { title: 'Test', date: new Date, body: 'Test' } ]
+ });
+
+ post.isModified('comments.0.body').should.be.false;
+ post.get('comments')[0].body = 'Woot';
+ post.isModified('comments').should.be.true;
+ post.isDirectModified('comments').should.be.false;
+ post.isModified('comments.0.body').should.be.true;
+ post.isDirectModified('comments.0.body').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a MongooseArray with atomics methods': function(){
+ // COMPLETEME
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+
+ post.isModified('owners').should.be.false;
+ post.get('owners').$push(new DocumentObjectId);
+ post.isModified('owners').should.be.true;
+
+ db.close();
+ },
+
+ 'test isModified on a MongooseArray with native methods': function(){
+ // COMPLETEME
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost()
+
+ post.isModified('owners').should.be.false;
+ post.get('owners').push(new DocumentObjectId);
+
+ db.close();
+ },
+
+ 'test isModified on a Mongoose Document - Modifying an existing record' : function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+
+ var doc = {
+ title : 'Test'
+ , slug : 'test'
+ , date : new Date
+ , meta : {
+ date : new Date
+ , visitors : 5
+ }
+ , published : true
+ , mixed : { x: [ { y: [1,'yes', 2] } ] }
+ , numbers : []
+ , owners : [new DocumentObjectId, new DocumentObjectId]
+ , comments : [
+ { title: 'Test', date: new Date, body: 'Test' }
+ , { title: 'Super', date: new Date, body: 'Cool' }
+ ]
+ };
+
+ BlogPost.create(doc, function (err, post) {
+ BlogPost.findById(post.id, function (err, postRead) {
+ db.close();
+ should.strictEqual(null, err);
+ //set the same data again back to the document.
+ //expected result, nothing should be set to modified
+ postRead.isModified('comments').should.be.false;
+ postRead.isNew.should.be.false;
+ postRead.set(postRead.toObject());
+
+ postRead.isModified('title').should.be.false;
+ postRead.isModified('slug').should.be.false;
+ postRead.isModified('date').should.be.false;
+ postRead.isModified('meta.date').should.be.false;
+ postRead.isModified('meta.visitors').should.be.false;
+ postRead.isModified('published').should.be.false;
+ postRead.isModified('mixed').should.be.false;
+ postRead.isModified('numbers').should.be.false;
+ postRead.isModified('owners').should.be.false;
+ postRead.isModified('comments').should.be.false;
+ postRead.comments[2] = { title: 'index' };
+ postRead.comments = postRead.comments;
+ postRead.isModified('comments').should.be.true;
+ });
+ });
+ },
+
+ // gh-662
+ 'test nested structure created by merging': function() {
+ 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) {
+ should.strictEqual(null, err);
+ Merged.findById(merged.id, function(err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.a.foo.should.eql('baz');
+ found.a.b.bar.should.eql('qux');
+ });
+ });
+ },
+
+ // gh-714
+ 'modified nested objects which contain MongoseNumbers should not cause a RangeError on save': function () {
+ 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) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+ m.nested.num = 5;
+ m.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // gh-714 pt 2
+ 'no RangeError on remove() of a doc with Number _id': function () {
+ 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) {
+ should.strictEqual(null, err);
+
+ MyModel.findById(35, function (err, doc) {
+ should.strictEqual(null, err);
+
+ doc.remove(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ // GH-342
+ 'over-writing a number should persist to the db': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({
+ meta: {
+ date : new Date
+ , visitors : 10
+ }
+ });
+
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ post.set('meta.visitors', 20);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post.id, function (err, found) {
+ should.strictEqual(null, err);
+ found.get('meta.visitors').valueOf().should.equal(20);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test defining a new method': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.cool().should.eql(post);
+ db.close();
+ },
+
+ 'test defining a static': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.woot().should.eql(BlogPost);
+ db.close();
+ },
+
+ 'test casting error': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost();
+
+ try {
+ post.init({
+ date: 'Test'
+ });
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ try {
+ post.set('title', 'Test');
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test nested casting error': function(){
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , threw = false;
+
+ var post = new BlogPost();
+
+ try {
+ post.init({
+ meta: {
+ date: 'Test'
+ }
+ });
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ try {
+ post.set('meta.date', 'Test');
+ } catch(e){
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test casting error in subdocuments': function(){
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test casting error when adding a subdocument': function(){
+ 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;
+ }
+
+ threw.should.be.false;
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(CastError);
+ db.close();
+ });
+ },
+
+ 'test validation': function(){
+ function dovalidate (val) {
+ this.asyncScope.should.equal('correct');
+ return true;
+ }
+
+ function dovalidateAsync (val, callback) {
+ this.scope.should.equal('correct');
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('simple', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation with custom message': function () {
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.simple.should.be.an.instanceof(ValidatorError);
+ err.errors.simple.message.should.equal('Validator "must be abc" failed for path simple');
+ post.errors.simple.message.should.equal('Validator "must be abc" failed for path simple');
+
+ post.set('simple', 'abc');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ // GH-272
+ 'test validation with Model.schema.path introspection': function () {
+ 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) {
+ err.errors.name.message.should.equal("Validator \"Name cannot be greater than 1 character\" failed for path name");
+ err.name.should.equal("ValidationError");
+ err.message.should.equal("Validation failed");
+ db.close();
+ });
+ },
+
+ 'test required validation for undefined values': function () {
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('simple', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ // GH-319
+ 'save callback should only execute once regardless of number of failed validations': function () {
+ 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();
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ (++timesCalled).should.eql(1);
+
+ (Object.keys(err.errors).length).should.eql(3);
+ err.errors.password.should.be.an.instanceof(ValidatorError);
+ err.errors.email.should.be.an.instanceof(ValidatorError);
+ err.errors.username.should.be.an.instanceof(ValidatorError);
+ err.errors.password.message.should.eql('Validator failed for path password');
+ err.errors.email.message.should.eql('Validator failed for path email');
+ err.errors.username.message.should.eql('Validator failed for path username');
+
+ (Object.keys(post.errors).length).should.eql(3);
+ post.errors.password.should.be.an.instanceof(ValidatorError);
+ post.errors.email.should.be.an.instanceof(ValidatorError);
+ post.errors.username.should.be.an.instanceof(ValidatorError);
+ post.errors.password.message.should.eql('Validator failed for path password');
+ post.errors.email.message.should.eql('Validator failed for path email');
+ post.errors.username.message.should.eql('Validator failed for path username');
+
+ });
+ },
+
+ 'test validation on a query result': function () {
+ 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) {
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.resultv = 'yeah';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ TestV.findOne({ _id: post.id }, function (err, found) {
+ should.strictEqual(err, null);
+ found.resultv.should.eql('yeah');
+ found.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ })
+ });
+ });
+ })
+ },
+
+ 'test required validation for previously existing null values': function () {
+ 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) {
+ should.strictEqual(err, null);
+
+ TestP.findOne({_id: f[0]._id}, function (err, found) {
+ should.strictEqual(err, null);
+ found.isNew.should.be.false;
+ should.strictEqual(found.get('previous'), null);
+
+ found.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ found.set('previous', 'yoyo');
+ found.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ })
+ })
+ });
+ },
+
+ 'test nested validation': function(){
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ post.set('nested.required', 'here');
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation in subdocuments': function(){
+ var Subdocs = new Schema({
+ required: { type: String, required: true }
+ });
+
+ mongoose.model('TestSubdocumentsValidation', new Schema({
+ items: [Subdocs]
+ }));
+
+ var db = start()
+ , TestSubdocumentsValidation = db.model('TestSubdocumentsValidation');
+
+ var post = new TestSubdocumentsValidation();
+
+ post.get('items').push({ required: '' });
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.required.should.be.an.instanceof(ValidatorError);
+ err.errors.required.message.should.eql('Validator "required" failed for path required');
+
+ post.get('items')[0].set('required', true);
+ post.save(function(err){
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test async validation': function(){
+ var executed = false;
+
+ function validator(v, fn){
+ setTimeout(function () {
+ executed = true;
+ fn(v !== 'test');
+ }, 50);
+ };
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.async.should.be.an.instanceof(ValidatorError);
+ err.errors.async.message.should.eql('Validator "async validator" failed for path async');
+ executed.should.be.true;
+ executed = false;
+
+ post.set('async', 'woot');
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test nested async validation': function(){
+ var executed = false;
+
+ function validator(v, fn){
+ setTimeout(function () {
+ executed = true;
+ fn(v !== 'test');
+ }, 50);
+ };
+
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.set('nested.async', 'woot');
+ post.validate(function(err){
+ executed.should.be.true;
+ should.equal(err, null);
+ executed = false;
+
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ })
+
+ });
+ },
+
+ 'test async validation in subdocuments': function(){
+ var executed = false;
+
+ function validator (v, fn) {
+ setTimeout(function(){
+ executed = true;
+ fn(v !== '');
+ }, 50);
+ };
+
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ executed.should.be.true;
+ executed = false;
+
+ post.get('items')[0].set({ required: 'here' });
+ post.save(function(err){
+ executed.should.be.true;
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ },
+
+ 'test validation without saving': function(){
+
+ mongoose.model('TestCallingValidation', new Schema({
+ item: { type: String, required: true }
+ }));
+
+ var db = start()
+ , TestCallingValidation = db.model('TestCallingValidation');
+
+ var post = new TestCallingValidation;
+
+ post.schema.path('item').isRequired.should.be.true;
+
+ should.strictEqual(post.isNew, true);
+
+ post.validate(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ should.strictEqual(post.isNew, true);
+
+ post.item = 'yo';
+ post.validate(function(err){
+ should.equal(err, null);
+ should.strictEqual(post.isNew, true);
+ db.close();
+ });
+ });
+
+ },
+
+ 'test setting required 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;
+
+ post.schema.path('result').isRequired.should.be.false;
+
+ db.close();
+ },
+
+ 'test defaults application': function(){
+ var now = Date.now();
+
+ mongoose.model('TestDefaults', new Schema({
+ date: { type: Date, default: now }
+ }));
+
+ var db = start()
+ , TestDefaults = db.model('TestDefaults');
+
+ var post = new TestDefaults();
+ post.get('date').should.be.an.instanceof(Date);
+ (+post.get('date')).should.eql(now);
+ db.close();
+ },
+
+ 'test "type" is allowed as a key': function(){
+ mongoose.model('TestTypeDefaults', new Schema({
+ type: { type: String, default: 'YES!' }
+ }));
+
+ var db = start()
+ , TestDefaults = db.model('TestTypeDefaults');
+
+ var post = new TestDefaults();
+ post.get('type').should.be.a('string');
+ post.get('type').should.eql('YES!');
+
+ // GH-402
+ var TestDefaults2 = db.model('TestTypeDefaults2', new Schema({
+ x: { y: { type: { type: String }, owner: String } }
+ }));
+
+ var post = new TestDefaults2;
+ post.x.y.type = "#402";
+ post.x.y.owner= "me";
+ post.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+
+ },
+
+ 'test nested defaults application': 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();
+ post.get('nested.date').should.be.an.instanceof(Date);
+ (+post.get('nested.date')).should.eql(now);
+ db.close();
+ },
+
+ 'test defaults application in subdocuments': 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');
+
+ var post = new TestSubdocumentsDefaults();
+ post.get('items').push({});
+ post.get('items')[0].get('date').should.be.an.instanceof(Date);
+ (+post.get('items')[0].get('date')).should.eql(now);
+ db.close();
+ },
+
+ // TODO: adapt this text to handle a getIndexes callback that's not unique to
+ // the mongodb-native driver.
+ 'test that indexes are ensured when the model is compiled': function(){
+ var Indexed = new Schema({
+ name : { type: String, index: true }
+ , last : String
+ , email : String
+ });
+
+ Indexed.index({ last: 1, email: 1 }, { unique: true });
+
+ var db = start()
+ , IndexedModel = db.model('IndexedModel', Indexed, 'indexedmodel' + random())
+ , assertions = 0;
+
+ IndexedModel.on('index', function(){
+ IndexedModel.collection.getIndexes(function(err, indexes){
+ should.strictEqual(err, null);
+
+ for (var i in indexes)
+ indexes[i].forEach(function(index){
+ if (index[0] == 'name')
+ assertions++;
+ if (index[0] == 'last')
+ assertions++;
+ if (index[0] == 'email')
+ assertions++;
+ });
+
+ assertions.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'test indexes on embedded documents': function () {
+ var BlogPosts = new Schema({
+ _id : { type: ObjectId, index: true }
+ , title : { type: String, index: true }
+ , desc : String
+ });
+
+ var User = new Schema({
+ name : { type: String, index: true }
+ , blogposts : [BlogPosts]
+ });
+
+ var db = start()
+ , UserModel = db.model('DeepIndexedModel', User, 'deepindexedmodel' + random())
+ , assertions = 0;
+
+ UserModel.on('index', function () {
+ UserModel.collection.getIndexes(function (err, indexes) {
+ should.strictEqual(err, null);
+
+ for (var i in indexes)
+ indexes[i].forEach(function(index){
+ if (index[0] == 'name')
+ assertions++;
+ if (index[0] == 'blogposts._id')
+ assertions++;
+ if (index[0] == 'blogposts.title')
+ assertions++;
+ });
+
+ assertions.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'compound indexes on embedded documents should be created': function () {
+ var BlogPosts = new Schema({
+ title : String
+ , desc : String
+ });
+
+ BlogPosts.index({ title: 1, desc: 1 });
+
+ var User = new Schema({
+ name : { type: String, index: true }
+ , blogposts : [BlogPosts]
+ });
+
+ var db = start()
+ , UserModel = db.model('DeepCompoundIndexModel', User, 'deepcompoundindexmodel' + random())
+ , found = 0;
+
+ UserModel.on('index', function () {
+ UserModel.collection.getIndexes(function (err, indexes) {
+ should.strictEqual(err, null);
+
+ for (var index in indexes) {
+ switch (index) {
+ case 'name_1':
+ case 'blogposts.title_1_blogposts.desc_1':
+ ++found;
+ break;
+ }
+ }
+
+ db.close();
+ found.should.eql(2);
+ });
+ });
+ },
+
+ 'test getters with same name on embedded documents not clashing': 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' }
+ });
+
+ post.author.name.should.eql('A');
+ post.subject.name.should.eql('B');
+ post.author.name.should.eql('A');
+
+ db.close();
+ },
+
+ 'test post save middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var called = 0;
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(0);
+ called++;
+ });
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(1);
+ called++;
+ });
+
+ schema.post('save', function (obj) {
+ obj.title.should.eql('Little Green Running Hood');
+ called.should.equal(2);
+ db.close();
+ });
+
+ var db = start()
+ , TestMiddleware = db.model('TestPostSaveMiddleware', schema);
+
+ var test = new TestMiddleware({ title: 'Little Green Running Hood'});
+
+ test.save(function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var called = 0;
+
+ schema.pre('init', function (next) {
+ called++;
+ next();
+ });
+
+ schema.pre('save', function (next) {
+ called++;
+ next(new Error('Error 101'));
+ });
+
+ schema.pre('remove', function (next) {
+ called++;
+ next();
+ });
+
+ mongoose.model('TestMiddleware', schema);
+
+ var db = start()
+ , TestMiddleware = db.model('TestMiddleware');
+
+ var test = new TestMiddleware();
+
+ test.init({
+ title: 'Test'
+ });
+
+ called.should.eql(1);
+
+ test.save(function(err){
+ err.should.be.an.instanceof(Error);
+ err.message.should.eql('Error 101');
+ called.should.eql(2);
+
+ test.remove(function(err){
+ should.strictEqual(err, null);
+ called.should.eql(3);
+ db.close();
+ });
+ });
+ },
+
+ 'test post init middleware': function () {
+ var schema = new Schema({
+ title: String
+ });
+
+ var preinit = 0
+ , postinit = 0
+
+ schema.pre('init', function (next) {
+ ++preinit;
+ next();
+ });
+
+ schema.post('init', function () {
+ ++postinit;
+ });
+
+ mongoose.model('TestPostInitMiddleware', schema);
+
+ var db = start()
+ , Test = db.model('TestPostInitMiddleware');
+
+ var test = new Test({ title: "banana" });
+
+ test.save(function(err){
+ should.strictEqual(err, null);
+
+ Test.findById(test._id, function (err, test) {
+ should.strictEqual(err, null);
+ preinit.should.eql(1);
+ postinit.should.eql(1);
+ test.remove(function(err){
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test update doc casting': function () {
+ 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) {
+ should.strictEqual(err, null);
+
+ BlogPost.update({ title: 1, _id: id }, { title: 2 }, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.get('title').should.eql('2');
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test $push casting': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.get('numbers').push('3');
+ post.get('numbers')[0].should.equal(3);
+ db.close();
+ },
+
+ 'test $pull casting': function () {
+ 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) {
+ found.get('numbers').length.should.equal(4);
+ found.get('numbers').$pull('3');
+ found.save( function (err) {
+ BlogPost.findById( found.get('_id'), function (err, found2) {
+ found2.get('numbers').length.should.equal(3);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test updating numbers atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , totalDocs = 4
+ , saveQueue = [];
+
+ var post = new BlogPost();
+ post.set('meta.visitors', 5);
+
+ post.save(function(err){
+ if (err) throw err;
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('meta.visitors').increment();
+ doc.get('meta.visitors').valueOf().should.be.equal(6);
+ save(doc);
+ });
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == 4)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ if (err) throw err;
+ doc.get('meta.visitors').valueOf().should.be.equal(9);
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test incrementing a number atomically with an arbitrary value': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost');
+
+ var post = new BlogPost();
+
+ post.meta.visitors = 0;
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ post.meta.visitors.increment(50);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (+doc.meta.visitors).should.eql(50);
+ db.close();
+ });
+ });
+ });
+ },
+
+ // GH-203
+ 'test changing a number non-atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.meta.visitors = 5;
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.meta.visitors -= 2;
+
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (+doc.meta.visitors).should.eql(3);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test saving subdocuments atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , totalDocs = 4
+ , saveQueue = [];
+
+ var post = new BlogPost();
+
+ post.save(function(err){
+ if (err) throw err;
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '1' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '2' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '3' });
+ save(doc);
+ });
+
+ BlogPost.findOne({ _id: post.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('comments').push({ title: '4' }, { title: '5' });
+ save(doc);
+ });
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == 4)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('comments').length.should.eql(5);
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '1';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '2';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '3';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '4';
+ }).should.be.true;
+
+ doc.get('comments').some(function(comment){
+ return comment.get('title') == '5';
+ }).should.be.true;
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ // GH-310
+ 'test setting a subdocument atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+
+ BlogPost.create({
+ comments: [{ title: 'first-title', body: 'first-body'}]
+ }, function (err, blog) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, agent1blog) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, agent2blog) {
+ should.strictEqual(null, err);
+ agent1blog.get('comments')[0].title = 'second-title';
+ agent1blog.save( function (err) {
+ should.strictEqual(null, err);
+ agent2blog.get('comments')[0].body = 'second-body';
+ agent2blog.save( function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(blog.id, function (err, foundBlog) {
+ should.strictEqual(null, err);
+ db.close();
+ var comment = foundBlog.get('comments')[0];
+ comment.title.should.eql('second-title');
+ comment.body.should.eql('second-body');
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test doubly nested array saving and loading': function(){
+ var Inner = new Schema({
+ arr: [Number]
+ });
+
+ var Outer = new Schema({
+ inner: [Inner]
+ });
+ mongoose.model('Outer', Outer);
+
+ var db = start();
+ var Outer = db.model('Outer', 'arr_test_' + random());
+
+ var outer = new Outer();
+ outer.inner.push({});
+ outer.save(function(err) {
+ should.strictEqual(err, null);
+ outer.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ Outer.findById(outer.get('_id'), function(err, found) {
+ should.strictEqual(err, null);
+ should.equal(1, found.inner.length);
+ found.inner[0].arr.push(5);
+ found.save(function(err) {
+ should.strictEqual(err, null);
+ found.get('_id').should.be.an.instanceof(DocumentObjectId);
+ Outer.findById(found.get('_id'), function(err, found2) {
+ db.close();
+ should.strictEqual(err, null);
+ should.equal(1, found2.inner.length);
+ should.equal(1, found2.inner[0].arr.length);
+ should.equal(5, found2.inner[0].arr[0]);
+ });
+ });
+ });
+ });
+ },
+
+ 'test updating multiple Number $pushes as a single $pushAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({}, function (err, t) {
+ t.nested.nums.push(1);
+ t.nested.nums.push(2);
+
+ t.nested.nums.should.have.length(2);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(2);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test updating at least a single $push and $pushAll as a single $pushAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({}, function (err, t) {
+ t.nested.nums.push(1);
+ t.nested.nums.$pushAll([2, 3]);
+
+ t.nested.nums.should.have.length(3);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(3);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(3);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test activePaths should be updated for nested modifieds': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pull(2);
+
+ t._activePaths.stateOf('nested.nums').should.equal('modify');
+ db.close();
+
+ });
+ },
+
+ '$pull should affect what you see in an array before a save': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+
+ t.nested.nums.should.have.length(4);
+
+ db.close();
+ });
+ },
+
+ '$pullAll should affect what you see in an array before a save': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pullAll([1, 2, 3]);
+
+ t.nested.nums.should.have.length(2);
+
+ db.close();
+ });
+ },
+
+ 'test updating multiple Number $pulls as a single $pullAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pull(2);
+
+ t.nested.nums.should.have.length(3);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(3);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(3);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'having both a pull and pullAll should default to pullAll': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('NestedPushes', schema);
+ var Temp = db.model('NestedPushes', collection);
+
+ Temp.create({nested: {nums: [1, 2, 3, 4, 5]}}, function (err, t) {
+ t.nested.nums.$pull(1);
+ t.nested.nums.$pullAll([2, 3]);
+
+ t.nested.nums.should.have.length(2);
+
+ t.save( function (err) {
+ should.strictEqual(null, err);
+ t.nested.nums.should.have.length(2);
+ Temp.findById(t._id, function (err, found) {
+ found.nested.nums.should.have.length(2);
+ db.close();
+ });
+ });
+ });
+ },
+
+ '$shift': function () {
+ var db = start()
+ , schema = new Schema({
+ nested: {
+ nums: [Number]
+ }
+ });
+
+ mongoose.model('TestingShift', schema);
+ var Temp = db.model('TestingShift', collection);
+
+ Temp.create({ nested: { nums: [1,2,3] }}, function (err, t) {
+ should.strictEqual(null, err);
+
+ Temp.findById(t._id, function (err, found) {
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(3);
+ found.nested.nums.$pop();
+ found.nested.nums.should.have.length(2);
+ found.nested.nums[0].should.eql(1);
+ found.nested.nums[1].should.eql(2);
+
+ found.save(function (err) {
+ should.strictEqual(null, err);
+ Temp.findById(t._id, function (err, found) {
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(2);
+ found.nested.nums[0].should.eql(1);
+ found.nested.nums[1].should.eql(2);
+ found.nested.nums.$shift();
+ found.nested.nums.should.have.length(1);
+ found.nested.nums[0].should.eql(2);
+
+ found.save(function (err) {
+ should.strictEqual(null, err);
+ Temp.findById(t._id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.nested.nums.should.have.length(1);
+ found.nested.nums[0].should.eql(2);
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test saving embedded arrays of Numbers atomically': function () {
+ var db = start()
+ , TempSchema = new Schema({
+ nums: [Number]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('Temp', TempSchema);
+ var Temp = db.model('Temp', collection);
+
+ var t = new Temp();
+
+ t.save(function(err){
+ if (err) throw err;
+
+ Temp.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('nums').push(1);
+ save(doc);
+ });
+
+ Temp.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('nums').push(2, 3);
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ Temp.findOne({ _id: t.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('nums').length.should.eql(3);
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '1';
+ }).should.be.true;
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '2';
+ }).should.be.true;
+
+ doc.get('nums').some(function(num){
+ return num.valueOf() == '3';
+ }).should.be.true;
+
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test saving embedded arrays of Strings atomically': function () {
+ var db = start()
+ , StrListSchema = new Schema({
+ strings: [String]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('StrList', StrListSchema);
+ var StrList = db.model('StrList');
+
+ var t = new StrList();
+
+ t.save(function(err){
+ if (err) throw err;
+
+ StrList.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('strings').push('a');
+ save(doc);
+ });
+
+ StrList.findOne({ _id: t.get('_id') }, function(err, doc){
+ if (err) throw err;
+ doc.get('strings').push('b', 'c');
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ if (err) throw err;
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ StrList.findOne({ _id: t.get('_id') }, function (err, doc) {
+ if (err) throw err;
+
+ doc.get('strings').length.should.eql(3);
+
+ doc.get('strings').some(function(str){
+ return str == 'a';
+ }).should.be.true;
+
+ doc.get('strings').some(function(str){
+ return str == 'b';
+ }).should.be.true;
+
+ doc.get('strings').some(function(str){
+ return str == 'c';
+ }).should.be.true;
+
+ db.close();
+ });
+ };
+ });
+ },
+
+ 'test saving embedded arrays of Buffers atomically': function () {
+ var db = start()
+ , BufListSchema = new Schema({
+ buffers: [Buffer]
+ })
+ , totalDocs = 2
+ , saveQueue = [];
+
+ mongoose.model('BufList', BufListSchema);
+ var BufList = db.model('BufList');
+
+ var t = new BufList();
+
+ t.save(function(err){
+ should.strictEqual(null, err);
+
+ BufList.findOne({ _id: t.get('_id') }, function(err, doc){
+ should.strictEqual(null, err);
+ doc.get('buffers').push(new Buffer([140]));
+ save(doc);
+ });
+
+ BufList.findOne({ _id: t.get('_id') }, function(err, doc){
+ should.strictEqual(null, err);
+ doc.get('buffers').push(new Buffer([141]), new Buffer([142]));
+ save(doc);
+ });
+
+
+ function save(doc) {
+ saveQueue.push(doc);
+ if (saveQueue.length == totalDocs)
+ saveQueue.forEach(function (doc) {
+ doc.save(function (err) {
+ should.strictEqual(null, err);
+ --totalDocs || complete();
+ });
+ });
+ };
+
+ function complete () {
+ BufList.findOne({ _id: t.get('_id') }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+
+ doc.get('buffers').length.should.eql(3);
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 140;
+ }).should.be.true;
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 141;
+ }).should.be.true;
+
+ doc.get('buffers').some(function(buf){
+ return buf[0] == 142;
+ }).should.be.true;
+
+ });
+ };
+ });
+ },
+
+ // GH-255
+ 'test updating an embedded document in an embedded array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({comments: [{title: 'woot'}]}, function (err, post) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, found) {
+ should.strictEqual(err, null);
+ found.comments[0].title.should.equal('woot');
+ found.comments[0].title = 'notwoot';
+ found.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(found._id, function (err, updated) {
+ db.close();
+ should.strictEqual(err, null);
+ updated.comments[0].title.should.equal('notwoot');
+ });
+ });
+ });
+ });
+ },
+
+ // GH-334
+ 'test updating an embedded array document to an Object value': function () {
+ var db = start()
+ , SubSchema = new Schema({
+ name : String ,
+ subObj : { subName : String }
+ });
+ var GH334Schema = new Schema ({ name : String , arrData : [ SubSchema] });
+
+ mongoose.model('GH334' , GH334Schema);
+ var AModel = db.model('GH334');
+ var instance = new AModel();
+
+ instance.set( { name : 'name-value' , arrData : [ { name : 'arrName1' , subObj : { subName : 'subName1' } } ] });
+ instance.save(function(err) {
+ AModel.findById(instance.id, function(err, doc) {
+ doc.arrData[0].set('subObj' , { subName : 'modified subName' });
+ doc.save(function(err) {
+ should.strictEqual(null, err);
+ AModel.findById(instance.id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.arrData[0].subObj.subName.should.eql('modified subName');
+ });
+ });
+ });
+ });
+ },
+
+ // GH-267
+ 'saving an embedded document twice should not push that doc onto the parent doc twice': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.comments.push({title: 'woot'});
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ post.comments.should.have.length(1);
+ BlogPost.findById(post.id, function (err, found) {
+ should.strictEqual(err, null);
+ found.comments.should.have.length(1);
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ post.comments.should.have.length(1);
+ BlogPost.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.comments.should.have.length(1);
+ });
+ });
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id shortcut function': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ var subdoc1 = post.comments[0];
+ var subdoc2 = post.comments[1];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ // test with an objectid
+ doc.comments.id(subdoc1.get('_id')).title.should.eql('woot');
+
+ // test with a string
+ var id = DocumentObjectId.toString(subdoc2._id);
+ doc.comments.id(id).title.should.eql('aaaa');
+
+ db.close();
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id with cast error': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ should.strictEqual(doc.comments.id(null), null);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test filtering an embedded array by the id shortcut with no match': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+ should.strictEqual(doc.comments.id(new DocumentObjectId), null);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test for removing a subdocument atomically': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'hahaha';
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments[0].remove();
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.should.have.length(1);
+ doc.comments[0].title.should.eql('aaaa');
+
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test for single pull embedded doc' : function() {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'hahaha';
+ post.comments.push({ title: 'woot' });
+ post.comments.push({ title: 'aaaa' });
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.pull(doc.comments[0]);
+ doc.comments.pull(doc.comments[0]);
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.comments.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'try saving mixed data': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , count = 3;
+
+ // string
+ var post = new BlogPost();
+ post.mixed = 'woot';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err) {
+ should.strictEqual(err, null);
+
+ --count || db.close();
+ });
+ });
+
+ // array
+ var post2 = new BlogPost();
+ post2.mixed = { name: "mr bungle", arr: [] };
+ post2.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post2._id, function (err, doc){
+ should.strictEqual(err, null);
+
+ Array.isArray(doc.mixed.arr).should.be.true;
+
+ doc.mixed = [{foo: 'bar'}];
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(doc._id, function (err, doc){
+ should.strictEqual(err, null);
+
+ Array.isArray(doc.mixed).should.be.true;
+ doc.mixed.push({ hello: 'world' });
+ doc.mixed.push([ 'foo', 'bar' ]);
+ doc.commit('mixed');
+
+ doc.save(function (err, doc) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post2._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.mixed[0].should.eql({ foo: 'bar' });
+ doc.mixed[1].should.eql({ hello: 'world' });
+ doc.mixed[2].should.eql(['foo','bar']);
+ --count || db.close();
+ });
+ });
+ });
+
+ // date
+ var post3 = new BlogPost();
+ post3.mixed = new Date;
+ post3.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post3._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.mixed.should.be.an.instanceof(Date);
+ --count || db.close();
+ });
+ });
+ });
+
+ });
+ });
+
+ },
+
+ // GH-200
+ 'try populating mixed data from the constructor': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost');
+
+ var post = new BlogPost({
+ mixed: {
+ type: 'test'
+ , github: 'rules'
+ , nested: {
+ number: 3
+ }
+ }
+ });
+
+ post.mixed.type.should.eql('test');
+ post.mixed.github.should.eql('rules');
+ post.mixed.nested.number.should.eql(3);
+
+ db.close();
+ },
+
+ 'test that we instantiate MongooseNumber in arrays': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.push(1, '2', 3);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ (~doc.numbers.indexOf(1)).should.not.eql(0);
+ (~doc.numbers.indexOf(2)).should.not.eql(0);
+ (~doc.numbers.indexOf(3)).should.not.eql(0);
+
+ db.close();
+ });
+ });
+ },
+
+ 'test removing from an array atomically using MongooseArray#remove': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.numbers.push(1, 2, 3);
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.remove('1');
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.should.have.length(2);
+ doc.numbers.remove('2', '3');
+
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.numbers.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test getting a virtual property via get(...)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost({
+ title: 'Letters from Earth'
+ , author: 'Mark Twain'
+ });
+
+ post.get('titleWithAuthor').should.equal('Letters from Earth by Mark Twain');
+
+ db.close();
+ },
+
+ 'test setting a virtual property': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain')
+ post.get('title').should.equal('Huckleberry Finn');
+ post.get('author').should.equal('Mark Twain');
+
+ db.close();
+ },
+
+ 'test getting a virtual property via shortcut getter': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost({
+ title: 'Letters from Earth'
+ , author: 'Mark Twain'
+ });
+
+ post.titleWithAuthor.should.equal('Letters from Earth by Mark Twain');
+
+ db.close();
+ },
+
+ // gh-685
+ 'getters should not be triggered at construction': function () {
+ var db = start()
+ , called = false
+
+ db.close();
+
+ var schema = new mongoose.Schema({
+ number: {
+ type:Number
+ , set: function(x){return x/2}
+ , get: function(x){
+ called = true;
+ return x*2;
+ }
+ }
+ });
+
+ var A = mongoose.model('gettersShouldNotBeTriggeredAtConstruction', schema);
+
+ var a = new A({ number: 100 });
+ called.should.be.false;
+ var num = a.number;
+ called.should.be.true;
+ num.valueOf().should.equal(100);
+ a.getValue('number').valueOf().should.equal(50);
+
+ called = false;
+ var b = new A;
+ b.init({ number: 50 });
+ called.should.be.false;
+ num = b.number;
+ called.should.be.true;
+ num.valueOf().should.equal(100);
+ b.getValue('number').valueOf().should.equal(50);
+ },
+
+ 'saving a doc with a set virtual property should persist the real properties but not the virtual property': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.set('titleWithAuthor', 'Huckleberry Finn by Mark Twain')
+ post.get('title').should.equal('Huckleberry Finn');
+ post.get('author').should.equal('Mark Twain');
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post.get('_id'), function (err, found) {
+ should.strictEqual(err, null);
+
+ found.get('title').should.equal('Huckleberry Finn');
+ found.get('author').should.equal('Mark Twain');
+ found.toObject().should.not.have.property('titleWithAuthor');
+ db.close();
+ });
+ });
+ },
+
+ 'test setting a pseudo-nested virtual property': 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'
+ }
+ });
+
+ person.get('name.full').should.equal('Michael Sorrentino');
+ person.set('name.full', 'The Situation');
+ person.get('name.first').should.equal('The');
+ person.get('name.last').should.equal('Situation');
+
+ person.name.full.should.equal('The Situation');
+ person.name.full = 'Michael Sorrentino';
+ person.name.first.should.equal('Michael');
+ person.name.last.should.equal('Sorrentino');
+
+ db.close();
+ },
+
+ 'test removing all documents from a collection via Model.remove': function () {
+ var db = start()
+ , collection = 'blogposts_' + random()
+ , BlogPost = db.model('BlogPost', collection)
+ , post = new BlogPost();
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.find({}, function (err, found) {
+ should.strictEqual(err, null);
+
+ found.length.should.equal(1);
+
+ BlogPost.remove({}, function (err) {
+ should.strictEqual(!!err, false);
+
+ BlogPost.find({}, function (err, found2) {
+ should.strictEqual(err, null);
+
+ found2.should.have.length(0);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ // GH-190
+ 'test shorcut getter for a type defined with { type: Native }': function () {
+ var schema = new Schema({
+ date: { type: Date }
+ });
+
+ mongoose.model('ShortcutGetterObject', schema);
+
+ var db = start()
+ , ShortcutGetter = db.model('ShortcutGetterObject', 'shortcut' + random())
+ , post = new ShortcutGetter();
+
+ post.set('date', Date.now());
+ post.date.should.be.an.instanceof(Date);
+
+ db.close();
+ },
+
+ 'test shortcut getter for a nested path': function () {
+ var schema = new Schema({
+ first: {
+ second: [Number]
+ }
+ });
+ mongoose.model('ShortcutGetterNested', schema);
+
+ var db = start()
+ , ShortcutGetterNested = db.model('ShortcutGetterNested', collection)
+ , doc = new ShortcutGetterNested();
+
+ doc.first.should.be.a('object');
+ doc.first.second.should.be.an.instanceof(MongooseArray);
+
+ db.close();
+ },
+
+ // GH-195
+ 'test that save on an unaltered model doesn\'t clear the document': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.title = 'woot';
+ post.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ // we deliberately make no alterations
+ doc.save(function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(doc._id, function (err, doc) {
+ should.strictEqual(err, null);
+
+ doc.title.should.eql('woot');
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'test that safe mode is the default and it works': function () {
+ var Human = new Schema({
+ name : String
+ , email : { type: String, unique: true }
+ });
+
+ mongoose.model('SafeHuman', Human, true);
+
+ var db = start()
+ , Human = db.model('SafeHuman', 'safehuman' + random());
+
+ var me = new Human({
+ name : 'Guillermo Rauch'
+ , email : 'rauchg@gmail.com'
+ });
+
+ me.save(function (err) {
+ should.strictEqual(err, null);
+
+ Human.findById(me._id, function (err, doc){
+ should.strictEqual(err, null);
+ doc.email.should.eql('rauchg@gmail.com');
+
+ var copycat = new Human({
+ name : 'Lionel Messi'
+ , email : 'rauchg@gmail.com'
+ });
+
+ copycat.save(function (err) {
+ /duplicate/.test(err.message).should.be.true;
+ err.should.be.an.instanceof(Error);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test that safe mode can be turned off': function () {
+ var Human = new Schema({
+ name : String
+ , email : { type: String, unique: true }
+ });
+
+ // turn it off
+ Human.set('safe', false);
+
+ mongoose.model('UnsafeHuman', Human, true);
+
+ var db = start()
+ , Human = db.model('UnsafeHuman', 'unsafehuman' + random());
+
+ var me = new Human({
+ name : 'Guillermo Rauch'
+ , email : 'rauchg@gmail.com'
+ });
+
+ me.save(function (err) {
+ should.strictEqual(err, null);
+
+ Human.findById(me._id, function (err, doc){
+ should.strictEqual(err, null);
+ doc.email.should.eql('rauchg@gmail.com');
+
+ var copycat = new Human({
+ name : 'Lionel Messi'
+ , email : 'rauchg@gmail.com'
+ });
+
+ copycat.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'passing null in pre hook works': function () {
+ var db = start();
+ var schema = new Schema({ name: String });
+
+ schema.pre('save', function (next) {
+ next(null); // <<-----
+ });
+
+ var S = db.model('S', schema, collection);
+ var s = new S({name: 'zupa'});
+
+ s.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+
+ },
+
+ 'pre hooks called on all sub levels': function () {
+ var db = start();
+
+ var grandSchema = new Schema({ name : String });
+ grandSchema.pre('save', function (next) {
+ this.name = 'grand';
+ next();
+ });
+
+ var childSchema = new Schema({ name : String, grand : [grandSchema]});
+ childSchema.pre('save', function (next) {
+ this.name = 'child';
+ next();
+ });
+
+ var schema = new Schema({ name: String, child : [childSchema] });
+
+ schema.pre('save', function (next) {
+ this.name = 'parent';
+ next();
+ });
+
+ var S = db.model('presave_hook', schema, 'presave_hook');
+ var s = new S({ name : 'a' , child : [ { name : 'b', grand : [{ name : 'c'}] } ]});
+
+ s.save(function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.name.should.eql('parent');
+ doc.child[0].name.should.eql('child');
+ doc.child[0].grand[0].name.should.eql('grand');
+ });
+ },
+
+ 'pre hooks error on all sub levels': function () {
+ var db = start();
+
+ var grandSchema = new Schema({ name : String });
+ grandSchema.pre('save', function (next) {
+ next(new Error('Error 101'));
+ });
+
+ var childSchema = new Schema({ name : String, grand : [grandSchema]});
+ childSchema.pre('save', function (next) {
+ this.name = 'child';
+ next();
+ });
+
+ var schema = new Schema({ name: String, child : [childSchema] });
+ schema.pre('save', function (next) {
+ this.name = 'parent';
+ next();
+ });
+
+ var S = db.model('presave_hook_error', schema, 'presave_hook_error');
+ var s = new S({ name : 'a' , child : [ { name : 'b', grand : [{ name : 'c'}] } ]});
+
+ s.save(function (err, doc) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ err.message.should.eql('Error 101');
+ });
+ },
+
+ 'test post hooks': function () {
+ var schema = new Schema({
+ title: String
+ })
+ , save = false
+ , remove = false
+ , init = false
+ , post = undefined;
+
+ schema.post('save', function (arg) {
+ arg.id.should.equal(post.id)
+ save = true;
+ });
+
+ schema.post('init', function () {
+ init = true;
+ });
+
+ schema.post('remove', function (arg) {
+ arg.id.should.eql(post.id)
+ remove = true;
+ });
+
+ mongoose.model('PostHookTest', schema);
+
+ var db = start()
+ , BlogPost = db.model('PostHookTest');
+
+ post = new BlogPost();
+
+ post.save(function (err) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ save.should.be.true;
+ BlogPost.findById(post._id, function (err, doc) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ init.should.be.true;
+
+ doc.remove(function (err) {
+ process.nextTick(function () {
+ should.strictEqual(err, null);
+ remove.should.be.true;
+ db.close();
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test count querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable count as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable count as promise'});
+ query.exec(function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ },
+
+ 'test update querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable update as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.update({title: 'interoperable update as promise'}, {title: 'interoperable update as promise delta'});
+ query.exec(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable update as promise delta'}, function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test findOne querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable findOne as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.findOne({title: 'interoperable findOne as promise'});
+ query.exec(function (err, found) {
+ should.strictEqual(err, null);
+ found.id.should.eql(created.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable find as promise'}
+ , {title: 'interoperable find as promise'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.find({title: 'interoperable find as promise'});
+ query.exec(function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(2);
+ found[0]._id.id.should.eql(createdOne._id.id);
+ found[1]._id.id.should.eql(createdTwo._id.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test remove querying via #run (aka #exec)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable remove as promise'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.remove({title: 'interoperable remove as promise'});
+ query.exec(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable remove as promise'}, function (err, count) {
+ db.close();
+ count.should.equal(0);
+ });
+ });
+ });
+ },
+
+ 'test changing query at the last minute via #run(op, callback)': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable ad-hoc as promise'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable ad-hoc as promise'});
+ query.exec('findOne', function (err, found) {
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(created._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test count querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable count as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable count as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ },
+
+ 'test update querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable update as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.update({title: 'interoperable update as promise 2'}, {title: 'interoperable update as promise delta 2'});
+ var promise = query.run();
+ promise.addBack(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable update as promise delta 2'}, function (err, count) {
+ should.strictEqual(err, null);
+ count.should.equal(1);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test findOne querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable findOne as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.findOne({title: 'interoperable findOne as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found.id.should.eql(created.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test find querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable find as promise 2'}
+ , {title: 'interoperable find as promise 2'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.find({title: 'interoperable find as promise 2'});
+ var promise = query.run();
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found.length.should.equal(2);
+ found[0].id;
+ found[1].id;
+ found[0]._id.should.eql(createdOne._id);
+ found[1]._id.should.eql(createdTwo._id);
+ db.close();
+ });
+ });
+ },
+
+ 'test remove querying via #run (aka #exec) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(
+ {title: 'interoperable remove as promise 2'}
+ , function (err, createdOne, createdTwo) {
+ should.strictEqual(err, null);
+ var query = BlogPost.remove({title: 'interoperable remove as promise 2'});
+ var promise = query.exec();
+ promise.addBack(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.count({title: 'interoperable remove as promise 2'}, function (err, count) {
+ count.should.equal(0);
+ db.close();
+ });
+ });
+ });
+ },
+
+ 'test changing query at the last minute via #run(op) with promise': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create({title: 'interoperable ad-hoc as promise 2'}, function (err, created) {
+ should.strictEqual(err, null);
+ var query = BlogPost.count({title: 'interoperable ad-hoc as promise 2'});
+ var promise = query.exec('findOne');
+ promise.addBack(function (err, found) {
+ should.strictEqual(err, null);
+ found._id.id.should.eql(created._id.id);
+ db.close();
+ });
+ });
+ },
+
+ 'test nested obj literal getter/setters': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var date = new Date;
+
+ var meta = {
+ date: date
+ , visitors: 5
+ };
+
+ var post = new BlogPost()
+ post.init({
+ meta: meta
+ });
+
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.date.should.be.an.instanceof(Date);
+
+ var threw = false;
+ var getter1;
+ var getter2;
+ var strmet;
+ try {
+ strmet = JSON.stringify(meta);
+ getter1 = JSON.stringify(post.get('meta'));
+ getter2 = JSON.stringify(post.meta);
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ getter1 = JSON.parse(getter1);
+ getter2 = JSON.parse(getter2);
+ getter1.visitors.should.eql(getter2.visitors);
+ getter1.date.should.eql(getter2.date);
+
+ post.meta.date = new Date - 1000;
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+
+ post.meta.visitors = 2;
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+
+ var newmeta = {
+ date: date - 2000
+ , visitors: 234
+ };
+
+ post.set(newmeta, 'meta');
+
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ (+post.meta.date).should.eql(date - 2000);
+ (+post.get('meta').date).should.eql(date - 2000);
+ (+post.meta.visitors).should.eql(234);
+ (+post.get('meta').visitors).should.eql(234);
+
+ // set object directly
+ post.meta = {
+ date: date - 3000
+ , visitors: 4815162342
+ };
+
+ post.meta.date.should.be.an.instanceof(Date);
+ post.get('meta').date.should.be.an.instanceof(Date);
+ post.meta.visitors.should.be.an.instanceof(MongooseNumber);
+ post.get('meta').visitors.should.be.an.instanceof(MongooseNumber);
+ (+post.meta.date).should.eql(date - 3000);
+ (+post.get('meta').date).should.eql(date - 3000);
+ (+post.meta.visitors).should.eql(4815162342);
+ (+post.get('meta').visitors).should.eql(4815162342);
+
+ db.close();
+ },
+
+ 'nested object property access works when root initd with null': function () {
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ }
+ });
+
+ mongoose.model('NestedStringA', schema);
+ var T = db.model('NestedStringA', collection);
+
+ var t = new T({ nest: null });
+
+ should.strictEqual(t.nest.st, null);
+ t.nest = { st: "jsconf rules" };
+ t.nest.toObject().should.eql({ st: "jsconf rules" });
+ t.nest.st.should.eql("jsconf rules");
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+
+ },
+
+ 'nested object property access works when root initd with undefined': function () {
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ }
+ });
+
+ mongoose.model('NestedStringB', schema);
+ var T = db.model('NestedStringB', collection);
+
+ var t = new T({ nest: undefined });
+
+ should.strictEqual(t.nest.st, undefined);
+ t.nest = { st: "jsconf rules" };
+ t.nest.toObject().should.eql({ st: "jsconf rules" });
+ t.nest.st.should.eql("jsconf rules");
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ db.close();
+ })
+ },
+
+ 're-saving object with pre-existing null nested object': function(){
+ var db = start()
+
+ var schema = new Schema({
+ nest: {
+ st: String
+ , yep: String
+ }
+ });
+
+ mongoose.model('NestedStringC', schema);
+ var T = db.model('NestedStringC', collection);
+
+ var t = new T({ nest: null });
+
+ t.save(function (err) {
+ should.strictEqual(err, null);
+
+ t.nest = { st: "jsconf rules", yep: "it does" };
+ t.save(function (err) {
+ should.strictEqual(err, null);
+
+ T.findById(t.id, function (err, t) {
+ should.strictEqual(err, null);
+ t.nest.st.should.eql("jsconf rules");
+ t.nest.yep.should.eql("it does");
+
+ t.nest = null;
+ t.save(function (err) {
+ should.strictEqual(err, null);
+ should.strictEqual(t._doc.nest, null);
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'pushing to a nested array of Mixed works on existing doc': function () {
+ var db = start();
+
+ mongoose.model('MySchema', new Schema({
+ nested: {
+ arrays: []
+ }
+ }));
+
+ var DooDad = db.model('MySchema')
+ , doodad = new DooDad({ nested: { arrays: [] } })
+ , date = 1234567890;
+
+ doodad.nested.arrays.push(["+10", "yup", date]);
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad.nested.arrays.toObject().should.eql([['+10','yup',date]]);
+
+ doodad.nested.arrays.push(["another", 1]);
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad
+ .nested
+ .arrays
+ .toObject()
+ .should.eql([['+10','yup',date], ["another", 1]]);
+
+ db.close();
+ });
+ });
+ })
+ });
+
+ },
+
+ 'directly setting nested props works when property is named "type"': function () {
+ var db = start();
+
+ function def () {
+ return [{ x: 1 }, { x: 2 }, { x:3 }]
+ }
+
+ mongoose.model('MySchema2', new Schema({
+ nested: {
+ type: { type: String, default: 'yep' }
+ , array: {
+ type: Array, default: def
+ }
+ }
+ }));
+
+ var DooDad = db.model('MySchema2', collection)
+ , doodad = new DooDad()
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+
+ doodad.nested.type.should.eql("yep");
+ doodad.nested.array.toObject().should.eql([{x:1},{x:2},{x:3}]);
+
+ doodad.nested.type = "nope";
+ doodad.nested.array = ["some", "new", "stuff"];
+
+ doodad.save(function (err) {
+ should.strictEqual(err, null);
+
+ DooDad.findById(doodad._id, function (err, doodad) {
+ should.strictEqual(err, null);
+ db.close();
+
+ doodad.nested.type.should.eql("nope");
+
+ doodad
+ .nested
+ .array
+ .toObject()
+ .should.eql(["some", "new", "stuff"]);
+
+ });
+ });
+ })
+ });
+ },
+
+ 'system.profile should be a default model': function () {
+ var Profile = mongoose.model('system.profile');
+ Profile.schema.paths.ts.should.be.a('object');
+ Profile.schema.paths.info.should.be.a('object');
+ Profile.schema.paths.millis.should.be.a('object');
+
+ Profile.schema.paths.op.should.be.a('object');
+ Profile.schema.paths.ns.should.be.a('object');
+ Profile.schema.paths.query.should.be.a('object');
+ Profile.schema.paths.updateobj.should.be.a('object');
+ Profile.schema.paths.ntoreturn.should.be.a('object');
+ Profile.schema.paths.nreturned.should.be.a('object');
+ Profile.schema.paths.nscanned.should.be.a('object');
+ Profile.schema.paths.responseLength.should.be.a('object');
+ Profile.schema.paths.client.should.be.a('object');
+ Profile.schema.paths.user.should.be.a('object');
+ Profile.schema.paths.idhack.should.be.a('object');
+ Profile.schema.paths.scanAndOrder.should.be.a('object');
+ Profile.schema.paths.keyUpdates.should.be.a('object');
+ should.strictEqual(undefined, Profile.schema.paths._id);
+ should.strictEqual(undefined, Profile.schema.virtuals.id);
+
+ var db = start();
+ Profile = db.model('system.profile');
+ db.close();
+ Profile.schema.paths.ts.should.be.a('object');
+ Profile.schema.paths.info.should.be.a('object');
+ Profile.schema.paths.millis.should.be.a('object');
+ Profile.schema.paths.op.should.be.a('object');
+ Profile.schema.paths.ns.should.be.a('object');
+ Profile.schema.paths.query.should.be.a('object');
+ Profile.schema.paths.updateobj.should.be.a('object');
+ Profile.schema.paths.ntoreturn.should.be.a('object');
+ Profile.schema.paths.nreturned.should.be.a('object');
+ Profile.schema.paths.nscanned.should.be.a('object');
+ Profile.schema.paths.responseLength.should.be.a('object');
+ Profile.schema.paths.client.should.be.a('object');
+ Profile.schema.paths.user.should.be.a('object');
+ Profile.schema.paths.idhack.should.be.a('object');
+ Profile.schema.paths.scanAndOrder.should.be.a('object');
+ Profile.schema.paths.keyUpdates.should.be.a('object');
+ should.strictEqual(undefined, Profile.schema.paths._id);
+ should.strictEqual(undefined, Profile.schema.virtuals.id);
+
+ // can override the default
+ db = start();
+ // reset Mongoose state
+ delete db.base.modelSchemas['system.profile']
+ delete db.base.models['system.profile']
+ delete db.models['system.profile'];
+ db.close();
+ // test
+ var over = db.model('system.profile', new Schema({ name: String }));
+ over.schema.paths.name.should.be.a('object');
+ should.strictEqual(undefined, over.schema.paths.ts);
+ // reset
+ delete db.base.modelSchemas['system.profile']
+ delete db.base.models['system.profile']
+ delete db.models['system.profile'];
+ },
+
+ 'setting profiling levels': function () {
+ var db = start();
+ db.setProfiling(3, function (err) {
+ err.message.should.eql('Invalid profiling level: 3');
+ db.setProfiling('fail', function (err) {
+ err.message.should.eql('Invalid profiling level: fail');
+ db.setProfiling(2, function (err, doc) {
+ should.strictEqual(err, null);
+ db.setProfiling(1, 50, function (err, doc) {
+ should.strictEqual(err, null);
+ doc.was.should.eql(2);
+ db.setProfiling(0, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.was.should.eql(1);
+ doc.slowms.should.eql(50);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test post hooks on embedded documents': function(){
+ var save = false,
+ init = false,
+ remove = false;
+
+ var EmbeddedSchema = new Schema({
+ title : String
+ });
+
+ var ParentSchema = new Schema({
+ embeds : [EmbeddedSchema]
+ });
+
+ EmbeddedSchema.post('save', function(next){
+ save = true;
+ });
+
+ // Don't know how to test those on a embedded document.
+ //EmbeddedSchema.post('init', function () {
+ //init = true;
+ //});
+
+ //EmbeddedSchema.post('remove', function () {
+ //remove = true;
+ //});
+
+ mongoose.model('Parent', ParentSchema);
+
+ var db = start(),
+ Parent = db.model('Parent');
+
+ var parent = new Parent();
+
+ parent.embeds.push({title: 'Testing post hooks for embedded docs'});
+
+ parent.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ save.should.be.true;
+ });
+ },
+
+ 'console.log shows helpful values': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var date = new Date(1305730951086);
+ var id0 = new DocumentObjectId('4dd3e169dbfb13b4570000b9');
+ var id1 = new DocumentObjectId('4dd3e169dbfb13b4570000b6');
+ var id2 = new DocumentObjectId('4dd3e169dbfb13b4570000b7');
+ var id3 = new DocumentObjectId('4dd3e169dbfb13b4570000b8');
+
+ var post = new BlogPost({
+ title: 'Test'
+ , _id: id0
+ , date: date
+ , numbers: [5,6,7]
+ , owners: [id1]
+ , meta: { visitors: 45 }
+ , comments: [
+ { _id: id2, title: 'my comment', date: date, body: 'this is a comment' },
+ { _id: id3, title: 'the next thang', date: date, body: 'this is a comment too!' }]
+ });
+
+ db.close();
+
+ var a = '{ meta: { visitors: 45 },\n numbers: [ 5, 6, 7 ],\n owners: [ 4dd3e169dbfb13b4570000b6 ],\n comments: \n [{ _id: 4dd3e169dbfb13b4570000b7,\n comments: [],\n body: \'this is a comment\',\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'my comment\' }\n { _id: 4dd3e169dbfb13b4570000b8,\n comments: [],\n body: \'this is a comment too!\',\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'the next thang\' }],\n _id: 4dd3e169dbfb13b4570000b9,\n date: Wed, 18 May 2011 15:02:31 GMT,\n title: \'Test\' }'
+
+ var out = post.inspect();
+ ;/meta: { visitors: 45 }/.test(out).should.be.true;
+ ;/numbers: \[ 5, 6, 7 \]/.test(out).should.be.true;
+ ;/date: Wed, 18 May 2011 15:02:31 GMT/.test(out).should.be.true;
+ ;/activePaths:/.test(out).should.be.false;
+ ;/_atomics:/.test(out).should.be.false;
+ },
+
+ 'path can be used as pathname': function () {
+ var db = start()
+ , P = db.model('pathnametest', new Schema({ path: String }))
+ db.close();
+
+ var threw = false;
+ try {
+ new P({ path: 'i should not throw' });
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ 'when mongo is down, save callback should fire with err if auto_reconnect is disabled': function () {
+ var db = start({ server: { auto_reconnect: false }});
+ var T = db.model('Thing', new Schema({ type: String }));
+ db.on('open', function () {
+ var t = new T({ type: "monster" });
+
+ var worked = false;
+ t.save(function (err) {
+ worked = true;
+ err.message.should.eql('no open connections');
+ });
+
+ process.nextTick(function () {
+ db.close();
+ });
+
+ setTimeout(function () {
+ worked.should.be.true;
+ }, 500);
+ });
+ },
+
+ //'when mongo is down, auto_reconnect should kick in and db operation should succeed': function () {
+ //var db = start();
+ //var T = db.model('Thing', new Schema({ type: String }));
+ //db.on('open', function () {
+ //var t = new T({ type: "monster" });
+
+ //var worked = false;
+ //t.save(function (err) {
+ //should.strictEqual(err, null);
+ //worked = true;
+ //});
+
+ //process.nextTick(function () {
+ //db.close();
+ //});
+
+ //setTimeout(function () {
+ //worked.should.be.true;
+ //}, 500);
+ //});
+ //},
+
+ 'subdocuments with changed values should persist the values': function () {
+ var db = start()
+ var Subdoc = new Schema({ name: String, mixed: Schema.Types.Mixed });
+ var T = db.model('SubDocMixed', new Schema({ subs: [Subdoc] }));
+
+ var t = new T({ subs: [{ name: "Hubot", mixed: { w: 1, x: 2 }}] });
+ t.subs[0].name.should.equal("Hubot");
+ t.subs[0].mixed.w.should.equal(1);
+ t.subs[0].mixed.x.should.equal(2);
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ should.strictEqual(null, err);
+ t.subs[0].name.should.equal("Hubot");
+ t.subs[0].mixed.w.should.equal(1);
+ t.subs[0].mixed.x.should.equal(2);
+
+ var sub = t.subs[0];
+ sub.name = "Hubot1";
+ sub.name.should.equal("Hubot1");
+ sub.isModified('name').should.be.true;
+ t.modified.should.be.true;
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ should.strictEqual(null, err);
+ should.strictEqual(t.subs[0].name, "Hubot1");
+
+ var sub = t.subs[0];
+ sub.mixed.w = 5;
+ sub.mixed.w.should.equal(5);
+ sub.isModified('mixed').should.be.false;
+ sub.commit('mixed');
+ sub.isModified('mixed').should.be.true;
+ sub.modified.should.be.true;
+ t.modified.should.be.true;
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(t.subs[0].mixed.w, 5);
+ })
+ })
+ });
+ });
+ })
+ })
+ },
+
+ 'RegExps can be saved': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost({ mixed: { rgx: /^asdf$/ } });
+ post.mixed.rgx.should.be.instanceof(RegExp);
+ post.mixed.rgx.source.should.eql('^asdf$');
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, post) {
+ db.close();
+ should.strictEqual(err, null);
+ post.mixed.rgx.should.be.instanceof(RegExp);
+ post.mixed.rgx.source.should.eql('^asdf$');
+ });
+ });
+ },
+
+ 'null defaults are allowed': function () {
+ var db = start();
+ var T = db.model('NullDefault', new Schema({ name: { type: String, default: null }}), collection);
+ var t = new T();
+
+ should.strictEqual(null, t.name);
+
+ t.save(function (err) {
+ should.strictEqual(null, err);
+
+ T.findById(t._id, function (err, t) {
+ db.close();
+ should.strictEqual(null, err);
+ should.strictEqual(null, t.name);
+ });
+ });
+ },
+
+ // GH-365, GH-390, GH-422
+ 'test that setters are used on embedded documents': function () {
+ var db = start();
+
+ function setLat (val) {
+ return parseInt(val);
+ }
+
+ var tick = 0;
+ function uptick () {
+ return ++tick;
+ }
+
+ var Location = new Schema({
+ lat: { type: Number, default: 0, set: setLat}
+ , long: { type: Number, set: uptick }
+ });
+
+ var Deal = new Schema({
+ title: String
+ , locations: [Location]
+ });
+
+ Location = db.model('Location', Location, 'locations_' + random());
+ Deal = db.model('Deal', Deal, 'deals_' + random());
+
+ var location = new Location({lat: 1.2, long: 10});
+ location.lat.valueOf().should.equal(1);
+ location.long.valueOf().should.equal(1);
+
+ var deal = new Deal({title: "My deal", locations: [{lat: 1.2, long: 33}]});
+ deal.locations[0].lat.valueOf().should.equal(1);
+ deal.locations[0].long.valueOf().should.equal(2);
+
+ deal.save(function (err) {
+ should.strictEqual(err, null);
+ Deal.findById(deal._id, function (err, deal) {
+ db.close();
+ should.strictEqual(err, null);
+ deal.locations[0].lat.valueOf().should.equal(1);
+ // GH-422
+ deal.locations[0].long.valueOf().should.equal(2);
+ });
+ });
+ },
+
+ // GH-289
+ 'test that pre-init middleware has access to the true ObjectId when used with querying': function () {
+ var db = start()
+ , PreInitSchema = new Schema({})
+ , preId;
+ PreInitSchema.pre('init', function (next) {
+ preId = this._id;
+ next();
+ });
+ var PreInit = db.model('PreInit', PreInitSchema, 'pre_inits' + random());
+
+ var doc = new PreInit();
+ doc.save( function (err) {
+ should.strictEqual(err, null);
+ PreInit.findById(doc._id, function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual(undefined, preId);
+ });
+ });
+ },
+
+ // Demonstration showing why GH-261 is a misunderstanding
+ 'a single instantiated document should be able to update its embedded documents more than once': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ var post = new BlogPost();
+ post.comments.push({title: 'one'});
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ post.comments[0].title.should.eql('one');
+ post.comments[0].title = 'two';
+ post.comments[0].title.should.eql('two');
+ post.save( function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, found) {
+ should.strictEqual(err, null);
+ db.close();
+ should.strictEqual(err, null);
+ found.comments[0].title.should.eql('two');
+ });
+ });
+ });
+ },
+
+ 'defining a method on a Schema corresponding to an EmbeddedDocument should generate an instance method for the ED': 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());
+
+ var c = new ChildA;
+ c.talk.should.be.a('function');
+
+ var p = new ParentA();
+ p.children.push({});
+ p.children[0].talk.should.be.a('function');
+ db.close();
+ },
+
+ 'Model#save should throw errors if a callback is not passed': function () {
+ var db = start();
+
+ var DefaultErrSchema = new Schema({});
+ DefaultErrSchema.pre('save', function (next) {
+ try {
+ next(new Error);
+ } catch (error) {
+ // throws b/c nothing is listening to the error event
+ db.close();
+ error.should.be.instanceof(Error);
+ }
+ });
+ var DefaultErr = db.model('DefaultErr1', DefaultErrSchema, 'default_err_' + random());
+ new DefaultErr().save();
+ },
+
+ 'Model#save should emit an error on its db if a callback is not passed to it': function () {
+ var db = start();
+
+ db.on('error', function (err) {
+ db.close();
+ err.should.be.an.instanceof(Error);
+ });
+
+ var DefaultErrSchema = new Schema({});
+ DefaultErrSchema.pre('save', function (next) {
+ next(new Error);
+ });
+ var DefaultErr = db.model('DefaultErr2', DefaultErrSchema, 'default_err_' + random());
+ new DefaultErr().save();
+ },
+
+ // once hooks-js merges our fix this will pass
+ 'calling next() after a thrown error should not work': function () {
+ var db = start();
+
+ var s = new Schema({});
+ s.methods.funky = function () {
+ should.strictEqual(false, true, 'reached unreachable code');
+ }
+
+ s.pre('funky', function (next) {
+ db.close();
+ try {
+ next(new Error);
+ } catch (error) {
+ error.should.be.instanceof(Error);
+ next();
+ // throws b/c nothing is listening to the error event
+ }
+ });
+ var Kaboom = db.model('wowNext2xAndThrow', s, 'next2xAndThrow' + random());
+ new Kaboom().funky();
+ },
+
+ 'ensureIndex error should emit on the db': function () {
+ var db = start();
+
+ db.on('error', function (err) {
+ /^E11000 duplicate key error index:/.test(err.message).should.equal(true);
+ db.close();
+ });
+
+ var schema = new Schema({ name: { type: String } })
+ , Test = db.model('IndexError', schema, "x"+random());
+
+ Test.create({ name: 'hi' }, { name: 'hi' }, function (err) {
+ should.strictEqual(err, null);
+ Test.schema.index({ name: 1 }, { unique: true });
+ Test.init();
+ });
+ },
+
+ 'backward compatibility with conflicted data in the db': function () {
+ var db = start();
+ var M = db.model('backwardDataConflict', new Schema({ namey: { first: String, last: String }}));
+ var m = new M({ namey: "[object Object]" });
+ m.namey = { first: 'GI', last: 'Joe' };// <-- should overwrite the string
+ m.save(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ should.strictEqual('GI', m.namey.first);
+ should.strictEqual('Joe', m.namey.last);
+ });
+ },
+
+ '#push should work on EmbeddedDocuments more than 2 levels deep': function () {
+ var db = start()
+ , Post = db.model('BlogPost', collection)
+ , Comment = db.model('CommentNesting', Comments, collection);
+
+ var p =new Post({ title: "comment nesting" });
+ var c1 =new Comment({ title: "c1" });
+ var c2 =new Comment({ title: "c2" });
+ var c3 =new Comment({ title: "c3" });
+
+ p.comments.push(c1);
+ c1.comments.push(c2);
+ c2.comments.push(c3);
+
+ p.save(function (err) {
+ should.strictEqual(err, null);
+
+ Post.findById(p._id, function (err, p) {
+ should.strictEqual(err, null);
+
+ var c4=new Comment({ title: "c4" });
+ p.comments[0].comments[0].comments[0].comments.push(c4);
+ p.save(function (err) {
+ should.strictEqual(err, null);
+
+ Post.findById(p._id, function (err, p) {
+ db.close();
+ should.strictEqual(err, null);
+ p.comments[0].comments[0].comments[0].comments[0].title.should.equal('c4');
+ });
+ });
+ });
+ })
+
+ },
+
+ 'test standalone invalidate': function() {
+ var db = start()
+ , InvalidateSchema = null
+ , Post = null
+ , post = null;
+
+ InvalidateSchema = new Schema({
+ prop: { type: String },
+ });
+
+ mongoose.model('InvalidateSchema', InvalidateSchema);
+
+ Post = db.model('InvalidateSchema');
+ post = new Post();
+ post.set({baz: 'val'});
+ post.invalidate('baz', 'reason');
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+
+ err.errors.baz.should.be.an.instanceof(ValidatorError);
+ err.errors.baz.message.should.equal('Validator "reason" failed for path baz');
+ err.errors.baz.type.should.equal('reason');
+ err.errors.baz.path.should.equal('baz');
+
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test simple validation middleware': function() {
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.baz.type.should.equal('bad');
+ err.errors.baz.path.should.equal('baz');
+
+ post.set('baz', 'good');
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test async validation middleware': function() {
+ 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();
+ }, 50);
+ next();
+ });
+
+ mongoose.model('AsyncValidationMiddleware', AsyncValidationMiddlewareSchema);
+
+ Post = db.model('AsyncValidationMiddleware');
+ post = new Post();
+ post.set({prop: 'bad'});
+
+ post.save(function(err){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ err.errors.prop.type.should.equal('bad');
+ err.errors.prop.path.should.equal('prop');
+
+ post.set('prop', 'good');
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'test complex validation middleware': function() {
+ 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();
+ }, 50);
+ 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){
+ err.should.be.an.instanceof(MongooseError);
+ err.should.be.an.instanceof(ValidationError);
+ Object.keys(err.errors).length.should.equal(4);
+ err.errors.baz.should.be.an.instanceof(ValidatorError);
+ err.errors.baz.type.should.equal('bad');
+ err.errors.baz.path.should.equal('baz');
+ err.errors.abc.should.be.an.instanceof(ValidatorError);
+ err.errors.abc.type.should.equal('must be abc');
+ err.errors.abc.path.should.equal('abc');
+ err.errors.test.should.be.an.instanceof(ValidatorError);
+ err.errors.test.type.should.equal('must also be abc');
+ err.errors.test.path.should.equal('test');
+ err.errors.required.should.be.an.instanceof(ValidatorError);
+ err.errors.required.type.should.equal('required');
+ err.errors.required.path.should.equal('required');
+
+ post.set({
+ baz: 'good',
+ abc: 'abc',
+ test: 'test',
+ required: 'here'
+ });
+
+ post.save(function(err){
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ },
+
+ 'Model.create can accept an array': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create([{ title: 'hi'}, { title: 'bye'}], function (err, post1, post2) {
+ db.close();
+ should.strictEqual(err, null);
+ post1.get('_id').should.be.an.instanceof(DocumentObjectId);
+ post2.get('_id').should.be.an.instanceof(DocumentObjectId);
+
+ post1.title.should.equal('hi');
+ post2.title.should.equal('bye');
+ });
+ },
+
+ 'Model.create when passed no docs still fires callback': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create(function (err, a) {
+ db.close();
+ should.strictEqual(err, null);
+ should.not.exist(a);
+ });
+ },
+
+ 'Model.create fires callback when an empty array is passed': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection);
+
+ BlogPost.create([], function (err, a) {
+ db.close();
+ should.strictEqual(err, null);
+ should.not.exist(a);
+ });
+ },
+
+ 'enhanced date casting test (datejs - gh-502)': function () {
+ 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) {
+ should.strictEqual(err, null);
+ M.findById(m._id, function (err, m) {
+ should.strictEqual(err, null);
+ m.save(function (err) {
+ should.strictEqual(err, null);
+ M.remove(function (err) {
+ delete Date.prototype.toObject;
+ db.close();
+ });
+ });
+ });
+ });
+ },
+
+ 'should not persist non-schema props': function () {
+ var db = start()
+ , B = db.model('BlogPost', collection)
+
+ var b = new B;
+ b.whateveriwant = 10;
+ b.save(function (err) {
+ should.strictEqual(null, err);
+ B.collection.findOne({ _id: b._id }, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ ;('whateveriwant' in doc).should.be.false;
+ });
+ });
+ },
+
+ 'should not throw range error when using Number _id and saving existing doc': function () {
+ var db =start();
+
+ var T = new Schema({ _id: Number, a: String });
+
+ var D = db.model('Testing691', T, 'asdf' + random());
+
+ var d = new D({ _id: 1 });
+ d.save(function (err) {
+ should.strictEqual(null, err);
+
+ D.findById(d._id, function (err, d) {
+ should.strictEqual(null, err);
+
+ d.a = 'yo';
+ d.save(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ },
+
+ 'number of affected docs should be returned when saving': function () {
+ var db = start()
+ var schema = new Schema({ name: String });
+ var S = db.model('AffectedDocsAreReturned', schema);
+ var s = new S({ name: 'aaron' });
+ s.save(function (err, doc, affected) {
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+ s.name = 'heckmanananananana';
+ s.save(function (err, doc, affected) {
+ db.close();
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/model.update.test.js b/node_modules/mongoose/test/model.update.test.js
new file mode 100644
index 0000000..8e795d5
--- /dev/null
+++ b/node_modules/mongoose/test/model.update.test.js
@@ -0,0 +1,526 @@
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , 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
+ , MongooseNumber = mongoose.Types.Number
+ , 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]
+});
+
+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();
+
+var strictSchema = new Schema({ name: String }, { strict: true });
+mongoose.model('UpdateStrictSchema', strictSchema);
+
+module.exports = {
+
+ 'test updating documents': function () {
+ var db = start()
+ , BlogPost = db.model('BlogPost', collection)
+ , title = 'Tobi ' + random()
+ , author = 'Brian ' + random()
+ , newTitle = 'Woot ' + random()
+ , id0 = new DocumentObjectId
+ , id1 = new DocumentObjectId
+
+ var post = new BlogPost();
+ post.set('title', title);
+ post.author = author;
+ post.meta.visitors = 0;
+ post.date = new Date;
+ post.published = true;
+ post.mixed = { x: 'ex' };
+ post.numbers = [4,5,6,7];
+ post.owners = [id0, id1];
+ post.comments = [{ body: 'been there' }, { body: 'done that' }];
+
+ post.save(function (err) {
+ should.strictEqual(err, null);
+ BlogPost.findById(post._id, function (err, cf) {
+ should.strictEqual(err, null);
+ cf.title.should.equal(title);
+ cf.author.should.equal(author);
+ cf.meta.visitors.valueOf().should.eql(0);
+ cf.date.should.eql(post.date);
+ cf.published.should.be.true;
+ cf.mixed.x.should.equal('ex');
+ cf.numbers.toObject().should.eql([4,5,6,7]);
+ cf.owners.length.should.equal(2);
+ cf.owners[0].toString().should.equal(id0.toString());
+ cf.owners[1].toString().should.equal(id1.toString());
+ cf.comments.length.should.equal(2);
+ cf.comments[0].body.should.eql('been there');
+ cf.comments[1].body.should.eql('done that');
+ should.exist(cf.comments[0]._id);
+ should.exist(cf.comments[1]._id);
+ cf.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ cf.comments[1]._id.should.be.an.instanceof(DocumentObjectId);
+
+ var update = {
+ title: newTitle // becomes $set
+ , $inc: { 'meta.visitors': 2 }
+ , $set: { date: new Date }
+ , published: false // becomes $set
+ , 'mixed': { x: 'ECKS', y: 'why' } // $set
+ , $pullAll: { 'numbers': [4, 6] }
+ , $pull: { 'owners': id0 }
+ , 'comments.1.body': 8 // $set
+ }
+
+ BlogPost.update({ title: title }, update, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, up) {
+ should.strictEqual(err, null);
+ up.title.should.equal(newTitle);
+ up.author.should.equal(author);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.date.toString().should.equal(update.$set.date.toString());
+ up.published.should.eql(false);
+ up.mixed.x.should.equal('ECKS');
+ up.mixed.y.should.equal('why');
+ up.numbers.toObject().should.eql([5,7]);
+ up.owners.length.should.equal(1);
+ up.owners[0].toString().should.eql(id1.toString());
+ up.comments[0].body.should.equal('been there');
+ up.comments[1].body.should.equal('8');
+ should.exist(up.comments[0]._id);
+ should.exist(up.comments[1]._id);
+ up.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ up.comments[1]._id.should.be.an.instanceof(DocumentObjectId);
+
+ var update2 = {
+ 'comments.body': 'fail'
+ }
+
+ BlogPost.update({ _id: post._id }, update2, function (err) {
+ should.strictEqual(!!err, true);
+ ;/^can't append to array using string field name \[body\]/.test(err.message).should.be.true;
+ BlogPost.findById(post, function (err, p) {
+ should.strictEqual(null, err);
+
+ var update3 = {
+ $pull: 'fail'
+ }
+
+ BlogPost.update({ _id: post._id }, update3, function (err) {
+ should.strictEqual(!!err, true);
+ ;/Invalid atomic update value/.test(err.message).should.be.true;
+
+ var update4 = {
+ $inc: { idontexist: 1 }
+ }
+
+ // should not overwrite doc when no valid paths are submitted
+ BlogPost.update({ _id: post._id }, update4, function (err) {
+ should.strictEqual(err, null);
+
+ BlogPost.findById(post._id, function (err, up) {
+ should.strictEqual(err, null);
+
+ up.title.should.equal(newTitle);
+ up.author.should.equal(author);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.date.toString().should.equal(update.$set.date.toString());
+ up.published.should.eql(false);
+ up.mixed.x.should.equal('ECKS');
+ up.mixed.y.should.equal('why');
+ up.numbers.toObject().should.eql([5,7]);
+ up.owners.length.should.equal(1);
+ up.owners[0].toString().should.eql(id1.toString());
+ up.comments[0].body.should.equal('been there');
+ up.comments[1].body.should.equal('8');
+ // non-schema data was still stored in mongodb
+ should.strictEqual(1, up._doc.idontexist);
+
+ update5(post);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+
+ function update5 (post) {
+ var update = {
+ comments: [{ body: 'worked great' }]
+ , $set: {'numbers.1': 100}
+ , $inc: { idontexist: 1 }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(err, null);
+
+ // get the underlying doc
+ BlogPost.collection.findOne({ _id: post._id }, function (err, doc) {
+ should.strictEqual(err, null);
+
+ var up = new BlogPost;
+ up.init(doc);
+ up.comments.length.should.equal(1);
+ up.comments[0].body.should.equal('worked great');
+ should.strictEqual(true, !! doc.comments[0]._id);
+ up.meta.visitors.valueOf().should.equal(2);
+ up.mixed.x.should.equal('ECKS');
+ up.numbers.toObject().should.eql([5,100]);
+ up.numbers[1].valueOf().should.eql(100);
+
+ doc.idontexist.should.equal(2);
+ doc.numbers[1].should.eql(100);
+
+ update6(post);
+ });
+ });
+ }
+
+ function update6 (post) {
+ var update = {
+ $pushAll: { comments: [{ body: 'i am number 2' }, { body: 'i am number 3' }] }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(3);
+ ret.comments[1].body.should.equal('i am number 2');
+ should.strictEqual(true, !! ret.comments[1]._id);
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[2].body.should.equal('i am number 3');
+ should.strictEqual(true, !! ret.comments[2]._id);
+ ret.comments[2]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update7(post);
+ })
+ });
+ }
+
+ // gh-542
+ function update7 (post) {
+ var update = {
+ $pull: { comments: { body: 'i am number 2' } }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[1].body.should.equal('i am number 3');
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update8(post);
+ })
+ });
+ }
+
+ // gh-479
+ function update8 (post) {
+ function a () {};
+ a.prototype.toString = function () { return 'MongoDB++' }
+ var crazy = new a;
+
+ var update = {
+ $addToSet: { 'comments.$.comments': { body: 'The Ring Of Power' } }
+ , $set: { 'comments.$.title': crazy }
+ }
+
+ BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[0].title.should.equal('MongoDB++');
+ should.strictEqual(true, !! ret.comments[0].comments);
+ ret.comments[0].comments.length.should.equal(1);
+ should.strictEqual(ret.comments[0].comments[0].body, 'The Ring Of Power');
+ ret.comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[0].comments[0]._id.should.be.an.instanceof(DocumentObjectId)
+ ret.comments[1].body.should.equal('i am number 3');
+ should.strictEqual(undefined, ret.comments[1].title);
+ ret.comments[1]._id.should.be.an.instanceof(DocumentObjectId)
+
+ update9(post);
+ })
+ });
+ }
+
+ // gh-479
+ function update9 (post) {
+ var update = {
+ $inc: { 'comments.$.newprop': '1' }
+ , $set: { date: (new Date).getTime() } // check for single val casting
+ }
+
+ BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret._doc.comments[0]._doc.newprop.should.equal(1);
+ should.strictEqual(undefined, ret._doc.comments[1]._doc.newprop);
+ ret.date.should.be.an.instanceof(Date);
+ ret.date.toString().should.equal(update.$set.date.toString());
+
+ update10(post, ret);
+ })
+ });
+ }
+
+ // gh-545
+ function update10 (post, last) {
+ var owner = last.owners[0];
+
+ var update = {
+ $addToSet: { 'owners': owner }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(1);
+ ret.owners[0].toString().should.eql(owner.toString());
+
+ update11(post, ret);
+ })
+ });
+ }
+
+ // gh-545
+ function update11 (post, last) {
+ var owner = last.owners[0]
+ , newowner = new DocumentObjectId
+
+ var update = {
+ $addToSet: { 'owners': { $each: [owner, newowner] }}
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(2);
+ ret.owners[0].toString().should.eql(owner.toString());
+ ret.owners[1].toString().should.eql(newowner.toString());
+
+ update12(post, newowner);
+ })
+ });
+ }
+
+ // gh-574
+ function update12 (post, newowner) {
+ var update = {
+ $pop: { 'owners': -1 }
+ , $unset: { title: 1 }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.owners.length.should.equal(1);
+ ret.owners[0].toString().should.eql(newowner.toString());
+ should.strictEqual(undefined, ret.title);
+
+ update13(post, ret);
+ })
+ });
+ }
+
+ function update13 (post, ret) {
+ var update = {
+ $set: {
+ 'comments.0.comments.0.date': '11/5/2011'
+ , 'comments.1.body': 9000
+ }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+ ret.comments[0].body.should.equal('worked great');
+ ret.comments[1].body.should.equal('9000');
+ ret.comments[0].comments[0].date.toString().should.equal(new Date('11/5/2011').toString())
+ ret.comments[1].comments.length.should.equal(0);
+
+ update14(post, ret);
+ })
+ });
+ }
+
+ // gh-542
+ function update14 (post, ret) {
+ var update = {
+ $pull: { comments: { _id: ret.comments[0].id } }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(1);
+ ret.comments[0].body.should.equal('9000');
+ update15(post, ret);
+ })
+ });
+ }
+
+ function update15 (post, ret) {
+ var update = {
+ $pull: { comments: { body: { $in: [ret.comments[0].body] }} }
+ }
+
+ BlogPost.update({ _id: post._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(0);
+ update16(post, ret);
+ })
+ });
+ }
+
+ function update16 (post, ret) {
+ ret.comments.$pushAll([{body: 'hi'}, {body:'there'}]);
+ ret.save(function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(2);
+
+ var update = {
+ $pull: { comments: { body: { $nin: ['there'] }} }
+ }
+
+ BlogPost.update({ _id: ret._id }, update, function (err) {
+ should.strictEqual(null, err);
+ BlogPost.findById(post, function (err, ret) {
+ db.close();
+ should.strictEqual(null, err);
+ ret.comments.length.should.equal(1);
+ })
+ });
+ })
+ });
+ }
+ },
+
+ // gh-699
+ 'Model._castUpdate should honor strict schemas': function () {
+ var db = start();
+ var S = db.model('UpdateStrictSchema');
+ db.close();
+
+ var doc = S.find()._castUpdate({ ignore: true });
+ Object.keys(doc.$set).length.should.equal(0);
+ },
+
+ 'Model.update should honor strict schemas': function () {
+ var db = start();
+ var S = db.model('UpdateStrictSchema');
+ var s = new S({ name: 'orange crush' });
+
+ s.save(function (err) {
+ should.strictEqual(null, err);
+
+ S.update({ _id: s._id }, { ignore: true }, function (err, affected) {
+ should.strictEqual(null, err);
+ affected.should.equal(1);
+
+ S.findById(s._id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ should.not.exist(doc.ignore);
+ should.not.exist(doc._doc.ignore);
+ });
+ });
+ });
+ },
+
+ 'model.update passes number of affected documents': function () {
+ var db = start()
+ , B = db.model('BlogPost', 'wwwwowowo'+random())
+
+ B.create({ title: 'one'},{title:'two'},{title:'three'}, function (err) {
+ should.strictEqual(null, err);
+ B.update({}, { title: 'newtitle' }, { multi: true }, function (err, affected) {
+ db.close();
+ should.strictEqual(null, err);
+ affected.should.equal(3);
+ });
+ });
+ }
+
+}
diff --git a/node_modules/mongoose/test/namedscope.test.js b/node_modules/mongoose/test/namedscope.test.js
new file mode 100644
index 0000000..0160404
--- /dev/null
+++ b/node_modules/mongoose/test/namedscope.test.js
@@ -0,0 +1,253 @@
+//Query.prototype.where(criteria, callback)
+//Query.prototype.where(path, val, callback)
+//
+//UserNS.namedScope({
+// twenties: Query.where('age').gte(20).lt(30)
+// , male: Query.where('gender', 'male')
+// , lastLogin: Query.where('lastLogin').get(+new Date - (24 * 3600 * 1000))
+//});
+//
+//UserNS.find(twenties, male, active, function (err, found) {
+//});
+//
+//// twenties.male OR twenties.active
+//UserNS.twenties.male.OR.twenties.active.find(callback);
+//UserNS.find(twenties.male, twenties.active, function (err, found) {
+//});
+//
+//UserNS.find(olderThan(20).male, olderThan(30).active, function (err, found) {
+//});
+//UserNS.twenties.male.active.remove(callback);
+
+/**
+ * Test dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , _24hours = 24 * 3600 * 1000;
+
+/**
+ * Setup.
+ */
+
+var UserNSSchema = new Schema({
+ age: Number
+ , gender: String
+ , lastLogin: Date
+});
+
+UserNSSchema.namedScope('olderThan', function (age) {
+ return this.where('age').gt(age);
+});
+
+UserNSSchema.namedScope('youngerThan', function (age) {
+ return this.where('age').lt(age);
+});
+
+UserNSSchema.namedScope('twenties').olderThan(19).youngerThan(30);
+
+UserNSSchema.namedScope('male').where('gender', 'male');
+
+UserNSSchema.namedScope('female').where('gender', 'female');
+
+UserNSSchema.namedScope('active', function () {
+ return this.where('lastLogin').gte(+new Date - _24hours)
+});
+
+mongoose.model('UserNS', UserNSSchema);
+
+// TODO Add in tests for using named scopes where findOne, update, remove
+module.exports = {
+ 'basic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'dynamic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21}
+ , {age: 22}
+ , {age: 19}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.olderThan(20).find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'named scopes built on top of dynamic named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21}
+ , {age: 22}
+ , {age: 19}
+ , function (err, _) {
+ should.strictEqual(err, null);
+ UserNS.twenties.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ });
+ }
+ );
+ },
+ 'chaining named scopes should work, for find': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}
+ , {age: 45, gender: 'male', lastLogin: +new Date}
+ , {age: 50, gender: 'female', lastLogin: +new Date}
+ , function (err, _, match, _) {
+ should.strictEqual(err, null);
+ UserNS.olderThan(40).active.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ found[0]._id.should.eql(match._id);
+ });
+ }
+ );
+ },
+
+ 'basic named scopes should work, for remove': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, _) {
+ UserNS.male.remove( function (err) {
+ should.strictEqual(!!err, false);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(0);
+ });
+ });
+ }
+ );
+ },
+
+ // TODO multi-updates
+ 'basic named scopes should work, for update': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {gender: 'male'}
+ , {gender: 'male'}
+ , {gender: 'female'}
+ , function (err, male1, male2, female1) {
+ should.strictEqual(err, null);
+ UserNS.male.update({gender: 'female'}, function (err) {
+ should.strictEqual(err, null);
+ UserNS.female.find( function (err, found) {
+ should.strictEqual(err, null);
+ found.should.have.length(2);
+ UserNS.male.find( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.should.have.length(1);
+ });
+ });
+ });
+ }
+ );
+ },
+
+ 'chained named scopes should work, for findOne': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 100, gender: 'male'}
+ , function (err, maleCentenarian) {
+ should.strictEqual(err, null);
+ UserNS.male.olderThan(99).findOne( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(maleCentenarian._id);
+ });
+ }
+ );
+ },
+
+ 'hybrid use of chained named scopes and ad hoc querying should work': function () {
+ var db = start()
+ , UserNS = db.model('UserNS', 'users_' + random());
+ UserNS.create(
+ {age: 100, gender: 'female'}
+ , function (err, femaleCentenarian) {
+ should.strictEqual(null, err);
+ UserNS.female.where('age').gt(99).findOne( function (err, found) {
+ db.close();
+ should.strictEqual(err, null);
+ found.id;
+ found._id.should.eql(femaleCentenarian._id);
+ });
+ }
+ );
+ },
+// 'using chained named scopes in a find': function () {
+// var db = start()
+// , UserNS = db.model('UserNS', 'users_' + random());
+// UserNS.create({age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}, function (err, _) {
+// should.strictEqual(err, null);
+// UserNS.create({age: 45, gender: 'male', lastLogin: +new Date}, function (err, match) {
+// should.strictEqual(err, null);
+// UserNS.create({age: 50, gender: 'female', lastLogin: +new Date}, function (err, _) {
+// should.strictEqual(err, null);
+// UserNS.find(olderThan(40).active.male, function (err, found) {
+// db.close();
+// should.strictEqual(err, null);
+// found.should.have.length(1);
+// found[0]._id.should.eql(match._id);
+// });
+// });
+// });
+// });
+// },
+// 'using multiple chained named scopes in a find to do an OR': function () {
+// var db = start()
+// , UserNS = db.model('UserNS', collection);
+// var db = start()
+// , UserNS = db.model('UserNS', collection);
+// UserNS.create(
+// {age: 21, gender: 'male', lastLogin: (+new Date) - _24hours - 3600}
+// , {age: 45, gender: 'male', lastLogin: +new Date}
+// , {age: 50, gender: 'female', lastLogin: +new Date}
+// , {age: 35, gender: 'female', lastLogin: +new Date}
+// , function (err, a, b, c, d) {
+// should.strictEqual(err, null);
+// UserNS.find(twenties.active.male, thirties.active.female, function (err, found) {
+// db.close();
+// should.strictEqual(err, null);
+// found.should.have.length(2);
+// });
+// }
+// );
+// },
+};
diff --git a/node_modules/mongoose/test/promise.test.js b/node_modules/mongoose/test/promise.test.js
new file mode 100644
index 0000000..6d23a55
--- /dev/null
+++ b/node_modules/mongoose/test/promise.test.js
@@ -0,0 +1,167 @@
+
+/**
+ * Module dependencies.
+ */
+
+var should = require('should')
+ , Promise = require('../lib/promise');
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that events fire right away after complete()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.on('complete', function (a, b) {
+ a.should.eql('1');
+ b.should.eql('2');
+ called++;
+ });
+
+ promise.complete('1', '2');
+
+ promise.on('complete', function (a, b) {
+ a.should.eql('1');
+ b.should.eql('2');
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test that events fire right away after error()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.on('err', function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.error(new Error('booyah'));
+
+ promise.on('err', function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test errback+callback from constructor': function (beforeExit) {
+ var promise = new Promise(function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ })
+ , called = 0;
+
+ promise.error(new Error('dawg'));
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test errback+callback after complete()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.complete('woot');
+
+ promise.addBack(function (err, data){
+ data.should.eql('woot');
+ called++;
+ });
+
+ promise.addBack(function (err, data){
+ should.strictEqual(err, null);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test errback+callback after error()ing': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.error(new Error('woot'));
+
+ promise.addBack(function (err){
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.addBack(function (err){
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ beforeExit(function () {
+ called.should.eql(2);
+ });
+ },
+
+ 'test addCallback shortcut': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.addCallback(function (woot) {
+ should.strictEqual(woot, undefined);
+ called++;
+ });
+
+ promise.complete();
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test addErrback shortcut': function (beforeExit) {
+ var promise = new Promise()
+ , called = 0;
+
+ promise.addErrback(function (err) {
+ err.should.be.an.instanceof(Error);
+ called++;
+ });
+
+ promise.error(new Error);
+
+ beforeExit(function () {
+ called.should.eql(1);
+ });
+ },
+
+ 'test return value of #on()': function () {
+ var promise = new Promise()
+ promise.on('jump', function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addCallback()': function () {
+ var promise = new Promise()
+ promise.addCallback(function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addErrback()': function () {
+ var promise = new Promise()
+ promise.addErrback(function(){}).should.be.an.instanceof(Promise);
+ },
+
+ 'test return value of #addBack()': function () {
+ var promise = new Promise()
+ promise.addBack(function(){}).should.be.an.instanceof(Promise);
+ }
+
+};
diff --git a/node_modules/mongoose/test/query.test.js b/node_modules/mongoose/test/query.test.js
new file mode 100644
index 0000000..427d6cc
--- /dev/null
+++ b/node_modules/mongoose/test/query.test.js
@@ -0,0 +1,890 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , Query = require('../lib/query')
+ , mongoose = start.mongoose
+ , DocumentObjectId = mongoose.Types.ObjectId
+ , Schema = mongoose.Schema
+ , should = require('should')
+
+var Comment = new Schema({
+ text: String
+});
+
+var Product = new Schema({
+ tags: {} // mixed
+ , array: Array
+ , ids: [Schema.ObjectId]
+ , strings: [String]
+ , numbers: [Number]
+ , comments: [Comment]
+});
+
+mongoose.model('Product', Product);
+mongoose.model('Comment', Comment);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+ 'test query.fields({a: 1, b: 1, c: 1})': function () {
+ var query = new Query();
+ query.fields({a: 1, b: 1, c: 1});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields({only: "a b c"})': function () {
+ var query = new Query();
+ query.fields({only: "a b c"});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields({only: ["a", "b", "c"]})': function () {
+ var query = new Query();
+ query.fields({only: ['a', 'b', 'c']});
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields("a b c")': function () {
+ var query = new Query();
+ query.fields("a b c");
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ 'test query.fields("a", "b", "c")': function () {
+ var query = new Query();
+ query.fields('a', 'b', 'c');
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.fields(['a', 'b', 'c'])": function () {
+ var query = new Query();
+ query.fields(['a', 'b', 'c']);
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "Query#fields should not over-ride fields set in prior calls to Query#fields": function () {
+ var query = new Query();
+ query.fields('a');
+ query._fields.should.eql({a: 1});
+ query.fields('b');
+ query._fields.should.eql({a: 1, b: 1});
+ },
+// "Query#fields should be able to over-ride fields set in prior calls to Query#fields if you specify override": function () {
+// var query = new Query();
+// query.fields('a');
+// query._fields.should.eql({a: 1});
+// query.override.fields('b');
+// query._fields.should.eql({b: 1});
+// }
+
+ "test query.only('a b c')": function () {
+ var query = new Query();
+ query.only("a b c");
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.only('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.only('a', 'b', 'c');
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "test query.only('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.only(['a', 'b', 'c']);
+ query._fields.should.eql({a: 1, b: 1, c: 1});
+ },
+ "Query#only should not over-ride fields set in prior calls to Query#only": function () {
+ var query = new Query();
+ query.only('a');
+ query._fields.should.eql({a: 1});
+ query.only('b');
+ query._fields.should.eql({a: 1, b: 1});
+ },
+
+ "test query.exclude('a b c')": function () {
+ var query = new Query();
+ query.exclude("a b c");
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "test query.exclude('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.exclude('a', 'b', 'c');
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "test query.exclude('a', 'b', 'c')": function () {
+ var query = new Query();
+ query.exclude(['a', 'b', 'c']);
+ query._fields.should.eql({a: 0, b: 0, c: 0});
+ },
+ "Query#exclude should not over-ride fields set in prior calls to Query#exclude": function () {
+ var query = new Query();
+ query.exclude('a');
+ query._fields.should.eql({a: 0});
+ query.exclude('b');
+ query._fields.should.eql({a: 0, b: 0});
+ },
+
+ 'test setting a condition via where': function () {
+ var query = new Query();
+ query.where('name', 'guillermo');
+ query._conditions.should.eql({name: 'guillermo'});
+ },
+
+ 'setting a value via equals': function () {
+ var query = new Query();
+ query.where('name').equals('guillermo');
+ query._conditions.should.eql({name: 'guillermo'});
+ },
+
+ 'test Query#gte where 2 arguments': function () {
+ var query = new Query();
+ query.gte('age', 18);
+ query._conditions.should.eql({age: {$gte: 18}});
+ },
+
+ 'test Query#gt where 2 arguments': function () {
+ var query = new Query();
+ query.gt('age', 17);
+ query._conditions.should.eql({age: {$gt: 17}});
+ },
+
+ 'test Query#lte where 2 arguments': function () {
+ var query = new Query();
+ query.lte('age', 65);
+ query._conditions.should.eql({age: {$lte: 65}});
+ },
+
+ 'test Query#lt where 2 arguments': function () {
+ var query = new Query();
+ query.lt('age', 66);
+ query._conditions.should.eql({age: {$lt: 66}});
+ },
+
+ 'test Query#gte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").gte(18);
+ query._conditions.should.eql({age: {$gte: 18}});
+ },
+
+ 'test Query#gt where 1 argument': function () {
+ var query = new Query();
+ query.where("age").gt(17);
+ query._conditions.should.eql({age: {$gt: 17}});
+ },
+
+ 'test Query#lte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").lte(65);
+ query._conditions.should.eql({age: {$lte: 65}});
+ },
+
+ 'test Query#lt where 1 argument': function () {
+ var query = new Query();
+ query.where("age").lt(66);
+ query._conditions.should.eql({age: {$lt: 66}});
+ },
+
+ 'test combined Query#lt and Query#gt': function () {
+ var query = new Query();
+ query.where("age").lt(66).gt(17);
+ query._conditions.should.eql({age: {$lt: 66, $gt: 17}});
+ },
+
+ 'test Query#lt on one path and Query#gt on another path on the same query': function () {
+ var query = new Query();
+ query
+ .where("age").lt(66)
+ .where("height").gt(5);
+ query._conditions.should.eql({age: {$lt: 66}, height: {$gt: 5}});
+ },
+
+ 'test Query#ne where 2 arguments': function () {
+ var query = new Query();
+ query.ne('age', 21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#gte where 1 argument': function () {
+ var query = new Query();
+ query.where("age").ne(21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#ne alias Query#notEqualTo': function () {
+ var query = new Query();
+ query.where('age').notEqualTo(21);
+ query._conditions.should.eql({age: {$ne: 21}});
+
+ query = new Query();
+ query.notEqualTo('age', 21);
+ query._conditions.should.eql({age: {$ne: 21}});
+ },
+
+ 'test Query#in where 2 arguments': function () {
+ var query = new Query();
+ query.in('age', [21, 25, 30]);
+ query._conditions.should.eql({age: {$in: [21, 25, 30]}});
+ },
+
+ 'test Query#in where 1 argument': function () {
+ var query = new Query();
+ query.where("age").in([21, 25, 30]);
+ query._conditions.should.eql({age: {$in: [21, 25, 30]}});
+ },
+
+ 'test Query#in where a non-array value not via where': function () {
+ var query = new Query();
+ query.in('age', 21);
+ query._conditions.should.eql({age: {$in: 21}});
+ },
+
+ 'test Query#in where a non-array value via where': function () {
+ var query = new Query();
+ query.where('age').in(21);
+ query._conditions.should.eql({age: {$in: 21}});
+ },
+
+ 'test Query#nin where 2 arguments': function () {
+ var query = new Query();
+ query.nin('age', [21, 25, 30]);
+ query._conditions.should.eql({age: {$nin: [21, 25, 30]}});
+ },
+
+ 'test Query#nin where 1 argument': function () {
+ var query = new Query();
+ query.where("age").nin([21, 25, 30]);
+ query._conditions.should.eql({age: {$nin: [21, 25, 30]}});
+ },
+
+ 'test Query#nin where a non-array value not via where': function () {
+ var query = new Query();
+ query.nin('age', 21);
+ query._conditions.should.eql({age: {$nin: 21}});
+ },
+
+ 'test Query#nin where a non-array value via where': function () {
+ var query = new Query();
+ query.where('age').nin(21);
+ query._conditions.should.eql({age: {$nin: 21}});
+ },
+
+ 'test Query#mod not via where, where [a, b] param': function () {
+ var query = new Query();
+ query.mod('age', [5, 2]);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod not via where, where a and b params': function () {
+ var query = new Query();
+ query.mod('age', 5, 2);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod via where, where [a, b] param': function () {
+ var query = new Query();
+ query.where("age").mod([5, 2]);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#mod via where, where a and b params': function () {
+ var query = new Query();
+ query.where("age").mod(5, 2);
+ query._conditions.should.eql({age: {$mod: [5, 2]}});
+ },
+
+ 'test Query#near via where, where [lat, long] param': function () {
+ var query = new Query();
+ query.where('checkin').near([40, -72]);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near via where, where lat and long params': function () {
+ var query = new Query();
+ query.where('checkin').near(40, -72);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near not via where, where [lat, long] param': function () {
+ var query = new Query();
+ query.near('checkin', [40, -72]);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#near not via where, where lat and long params': function () {
+ var query = new Query();
+ query.near('checkin', 40, -72);
+ query._conditions.should.eql({checkin: {$near: [40, -72]}});
+ },
+
+ 'test Query#maxDistance via where': function () {
+ var query = new Query();
+ query.where('checkin').near([40, -72]).maxDistance(1);
+ query._conditions.should.eql({checkin: {$near: [40, -72], $maxDistance: 1}});
+ query = new Query();
+ query.where('checkin').near([40, -72]).$maxDistance(1);
+ query._conditions.should.eql({checkin: {$near: [40, -72], $maxDistance: 1}});
+ },
+
+ 'test Query#wherein.box not via where': function () {
+ var query = new Query();
+ query.wherein.box('gps', {ll: [5, 25], ur: [10, 30]});
+ query._conditions.should.eql({gps: {$within: {$box: [[5, 25], [10, 30]]}}});
+ },
+
+ 'test Query#wherein.box via where': function () {
+ var query = new Query();
+ query.where('gps').wherein.box({ll: [5, 25], ur: [10, 30]});
+ query._conditions.should.eql({gps: {$within: {$box: [[5, 25], [10, 30]]}}});
+ },
+
+ 'test Query#wherein.center not via where': function () {
+ var query = new Query();
+ query.wherein.center('gps', {center: [5, 25], radius: 5});
+ query._conditions.should.eql({gps: {$within: {$center: [[5, 25], 5]}}});
+ },
+
+ 'test Query#wherein.center not via where': function () {
+ var query = new Query();
+ query.where('gps').wherein.center({center: [5, 25], radius: 5});
+ query._conditions.should.eql({gps: {$within: {$center: [[5, 25], 5]}}});
+ },
+
+ 'test Query#exists where 0 arguments via where': function () {
+ var query = new Query();
+ query.where("username").exists();
+ query._conditions.should.eql({username: {$exists: true}});
+ },
+
+ 'test Query#exists where 1 argument via where': function () {
+ var query = new Query();
+ query.where("username").exists(false);
+ query._conditions.should.eql({username: {$exists: false}});
+ },
+
+ 'test Query#exists where 1 argument not via where': function () {
+ var query = new Query();
+ query.exists('username');
+ query._conditions.should.eql({username: {$exists: true}});
+ },
+
+ 'test Query#exists where 1 argument not via where': function () {
+ var query = new Query();
+ query.exists("username", false);
+ query._conditions.should.eql({username: {$exists: false}});
+ },
+
+ // TODO $not
+
+ 'test Query#all via where': function () {
+ var query = new Query();
+ query.where('pets').all(['dog', 'cat', 'ferret']);
+ query._conditions.should.eql({pets: {$all: ['dog', 'cat', 'ferret']}});
+ },
+
+ 'test Query#all not via where': function () {
+ var query = new Query();
+ query.all('pets', ['dog', 'cat', 'ferret']);
+ query._conditions.should.eql({pets: {$all: ['dog', 'cat', 'ferret']}});
+ },
+
+ 'test strict array equivalence condition via Query#find': function () {
+ var query = new Query();
+ query.find({'pets': ['dog', 'cat', 'ferret']});
+ query._conditions.should.eql({pets: ['dog', 'cat', 'ferret']});
+ },
+
+ '#find with no args should not throw': function () {
+ var threw = false;
+ var q = new Query();
+
+ try {
+ q.find();
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+ },
+
+ // TODO Check key.index queries
+
+ 'test Query#size via where': function () {
+ var query = new Query();
+ query.where('collection').size(5);
+ query._conditions.should.eql({collection: {$size: 5}});
+ },
+
+ 'test Query#size not via where': function () {
+ var query = new Query();
+ query.size('collection', 5);
+ query._conditions.should.eql({collection: {$size: 5}});
+ },
+
+ 'test Query#slice via where, where just positive limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice via where, where just negative limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(-5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice via where, where [skip, limit] param': function () {
+ var query = new Query();
+ query.where('collection').slice([14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where skip and limit params': function () {
+ var query = new Query();
+ query.where('collection').slice(14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where just positive limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice via where, where just negative limit param': function () {
+ var query = new Query();
+ query.where('collection').slice(-5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice via where, where the [skip, limit] param': function () {
+ var query = new Query();
+ query.where('collection').slice([14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice via where, where the skip and limit params': function () {
+ var query = new Query();
+ query.where('collection').slice(14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+
+ 'test Query#slice not via where, where just positive limit param': function () {
+ var query = new Query();
+ query.slice('collection', 5);
+ query._fields.should.eql({collection: {$slice: 5}});
+ },
+
+ 'test Query#slice not via where, where just negative limit param': function () {
+ var query = new Query();
+ query.slice('collection', -5);
+ query._fields.should.eql({collection: {$slice: -5}});
+ },
+
+ 'test Query#slice not via where, where [skip, limit] param': function () {
+ var query = new Query();
+ query.slice('collection', [14, 10]); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#slice not via where, where skip and limit params': function () {
+ var query = new Query();
+ query.slice('collection', 14, 10); // Return the 15th through 25th
+ query._fields.should.eql({collection: {$slice: [14, 10]}});
+ },
+
+ 'test Query#elemMatch not via where': function () {
+ var query = new Query();
+ query.elemMatch('comments', {author: 'bnoguchi', votes: {$gte: 5}});
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch not via where, where block notation': function () {
+ var query = new Query();
+ query.elemMatch('comments', function (elem) {
+ elem.where('author', 'bnoguchi')
+ elem.where('votes').gte(5);
+ });
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch via where': function () {
+ var query = new Query();
+ query.where('comments').elemMatch({author: 'bnoguchi', votes: {$gte: 5}});
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+ 'test Query#elemMatch via where, where block notation': function () {
+ var query = new Query();
+ query.where('comments').elemMatch(function (elem) {
+ elem.where('author', 'bnoguchi')
+ elem.where('votes').gte(5);
+ });
+ query._conditions.should.eql({comments: {$elemMatch: {author: 'bnoguchi', votes: {$gte: 5}}}});
+ },
+
+
+ 'test Query#$where where a function arg': function () {
+ var query = new Query();
+ function filter () {
+ return this.lastName === this.firstName;
+ }
+ query.$where(filter);
+ query._conditions.should.eql({$where: filter});
+ },
+
+ 'test Query#where where a javascript string arg': function () {
+ var query = new Query();
+ query.$where('this.lastName === this.firstName');
+ query._conditions.should.eql({$where: 'this.lastName === this.firstName'});
+ },
+
+ 'test Query#limit': function () {
+ var query = new Query();
+ query.limit(5);
+ query.options.limit.should.equal(5);
+ },
+
+ 'test Query#skip': function () {
+ var query = new Query();
+ query.skip(9);
+ query.options.skip.should.equal(9);
+ },
+
+ 'test Query#sort': function () {
+ var query = new Query();
+ query.sort('a', 1, 'c', -1, 'b', 1);
+ query.options.sort.should.eql([['a', 1], ['c', -1], ['b', 1]]);
+ },
+
+ 'test Query#asc and Query#desc': function () {
+ var query = new Query();
+ query.asc('a', 'z').desc('c', 'v').asc('b');
+ query.options.sort.should.eql([['a', 1], ['z', 1], ['c', -1], ['v', -1], ['b', 1]]);
+ },
+
+ 'Query#or': function () {
+ var query = new Query;
+ query.find({ $or: [{x:1},{x:2}] });
+ query._conditions.$or.length.should.equal(2);
+ query.$or([{y:"We're under attack"}, {z:47}]);
+ query._conditions.$or.length.should.equal(4);
+ query._conditions.$or[3].z.should.equal(47);
+ query.or({z:"phew"});
+ query._conditions.$or.length.should.equal(5);
+ query._conditions.$or[3].z.should.equal(47);
+ query._conditions.$or[4].z.should.equal("phew");
+ },
+
+ 'test running an empty Query should not throw': function () {
+ var query = new Query();
+ var threw = false;
+
+ try {
+ query.exec();
+ } catch (err) {
+ threw = true;
+ }
+
+ threw.should.eql(false);
+ },
+
+ 'test casting an array set to mixed type works': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var params = { _id: new DocumentObjectId, tags: { $in: [ 4, 8, 15, 16 ] }};
+
+ query.cast(Product, params);
+
+ params.tags.$in.should.eql([4,8,15,16]);
+ db.close();
+ },
+
+ //'throwing inside a query callback should not execute the callback again': function () {
+ //var query = new Query();
+ //var db = start();
+ //var Product = db.model('Product');
+
+ //var threw = false;
+ //Product.find({}, function (err) {
+ //if (!threw) {
+ //db.close();
+ //threw = true;
+ //throw new Error("Double callback");
+ //}
+
+ //should.strictEqual(err, null, 'Double callback detected');
+ //});
+ //},
+
+ 'Query#find $ne should not cast single value to array for schematype of Array': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var id = new DocumentObjectId;
+ var castedComment = { _id: id, text: 'hello there' };
+ var comment = new Comment(castedComment);
+
+ var params = {
+ array: { $ne: 5 }
+ , ids: { $ne: id }
+ , comments: { $ne: comment }
+ , strings: { $ne: 'Hi there' }
+ , numbers: { $ne: 10000 }
+ };
+
+ query.cast(Product, params);
+ params.array.$ne.should.equal(5);
+ params.ids.$ne.should.eql(id);
+ params.comments.$ne._id.toHexString();
+ params.comments.$ne.should.eql(castedComment);
+ params.strings.$ne.should.eql('Hi there');
+ params.numbers.$ne.should.eql(10000);
+
+ params.array.$ne = [5];
+ params.ids.$ne = [id];
+ params.comments.$ne = [comment];
+ params.strings.$ne = ['Hi there'];
+ params.numbers.$ne = [10000];
+ query.cast(Product, params);
+ params.array.$ne.should.be.instanceof(Array);
+ params.array.$ne[0].should.eql(5);
+ params.ids.$ne.should.be.instanceof(Array);
+ params.ids.$ne[0].toString().should.eql(id.toString());
+ params.comments.$ne.should.be.instanceof(Array);
+ params.comments.$ne[0].should.eql(castedComment);
+ params.strings.$ne.should.be.instanceof(Array);
+ params.strings.$ne[0].should.eql('Hi there');
+ params.numbers.$ne.should.be.instanceof(Array);
+ params.numbers.$ne[0].should.eql(10000);
+ },
+
+ 'Querying a subdocument array with $ne: null should not throw': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var params = {
+ comments: { $ne: null }
+ };
+
+ query.cast(Product, params);
+ should.strictEqual(params.comments.$ne, null);
+ },
+
+ 'Query#find should not cast single value to array for schematype of Array': function () {
+ var query = new Query();
+ var db = start();
+ var Product = db.model('Product');
+ var Comment = db.model('Comment');
+ db.close();
+
+ var id = new DocumentObjectId;
+ var castedComment = { _id: id, text: 'hello there' };
+ var comment = new Comment(castedComment);
+
+ var params = {
+ array: 5
+ , ids: id
+ , comments: comment
+ , strings: 'Hi there'
+ , numbers: 10000
+ };
+
+ query.cast(Product, params);
+ params.array.should.equal(5);
+ params.ids.should.eql(id);
+ params.comments._id.toHexString();
+ params.comments.should.eql(castedComment);
+ params.strings.should.eql('Hi there');
+ params.numbers.should.eql(10000);
+
+ params.array = [5];
+ params.ids = [id];
+ params.comments = [comment];
+ params.strings = ['Hi there'];
+ params.numbers = [10000];
+ query.cast(Product, params);
+ params.array.should.be.instanceof(Array);
+ params.array[0].should.eql(5);
+ params.ids.should.be.instanceof(Array);
+ params.ids[0].toString().should.eql(id.toString());
+ params.comments.should.be.instanceof(Array);
+ params.comments[0].should.eql(castedComment);
+ params.strings.should.be.instanceof(Array);
+ params.strings[0].should.eql('Hi there');
+ params.numbers.should.be.instanceof(Array);
+ params.numbers[0].should.eql(10000);
+ },
+
+ 'distinct Query op should be "distinct"': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ new Query().bind(Product, 'distinct').distinct('blah', function(){
+ db.close();
+ }).op.should.equal('distinct');
+ },
+
+ 'Query without a callback works': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ new Query().bind(Product, 'count').count();
+ setTimeout(db.close.bind(db), 1000);
+ },
+
+ '#findOne should set the op when callback is passed': function () {
+ var db = start();
+ var query = new Query();
+ var Product = db.model('Product');
+ var q = new Query().bind(Product, 'distinct');
+ q.op.should.equal('distinct');
+ q.findOne();
+ q.op.should.equal('findOne');
+ db.close();
+ },
+
+ 'querying/updating with model instance containing embedded docs should work (#454)': function () {
+ var db = start();
+ var Product = db.model('Product');
+
+ var proddoc = { comments: [{ text: 'hello' }] };
+ var prod2doc = { comments: [{ text: 'goodbye' }] };
+
+ var prod = new Product(proddoc);
+ var prod2 = new Product(prod2doc);
+
+ prod.save(function (err) {
+ should.strictEqual(err, null);
+
+ Product.findOne(prod, function (err, product) {
+ should.strictEqual(err, null);
+ product.comments.length.should.equal(1);
+ product.comments[0].text.should.equal('hello');
+
+ Product.update(product, prod2doc, function (err) {
+ should.strictEqual(err, null);
+
+ Product.collection.findOne({ _id: product._id }, function (err, doc) {
+ db.close();
+ should.strictEqual(err, null);
+ doc.comments.length.should.equal(1);
+ // ensure hidden private props were not saved to db
+ doc.comments[0].should.not.have.ownProperty('parentArr');
+ doc.comments[0].text.should.equal('goodbye');
+ });
+ });
+ });
+ });
+ },
+
+ 'optionsForExecute should retain key order': function () {
+ // this is important for query hints
+ var hint = { x: 1, y: 1, z: 1 };
+ var a = JSON.stringify({ hint: hint, safe: true});
+
+ var q = new Query;
+ q.hint(hint);
+
+ var options = q._optionsForExec({ options: { safe: true } });
+
+ a.should.equal(JSON.stringify(options));
+ },
+
+ // Advanced Query options
+
+ 'test Query#maxscan': function () {
+ var query = new Query();
+ query.maxscan(100);
+ query.options.maxscan.should.equal(100);
+ },
+
+ 'test Query#slaveOk': function () {
+ var query = new Query();
+ query.slaveOk();
+ query.options.slaveOk.should.be.true;
+
+ var query = new Query();
+ query.slaveOk(true);
+ query.options.slaveOk.should.be.true;
+
+ var query = new Query();
+ query.slaveOk(false);
+ query.options.slaveOk.should.be.false;
+ },
+
+ 'test Query#tailable': function () {
+ var query = new Query();
+ query.tailable();
+ query.options.tailable.should.be.true;
+
+ var query = new Query();
+ query.tailable(true);
+ query.options.tailable.should.be.true;
+
+ var query = new Query();
+ query.tailable(false);
+ query.options.tailable.should.be.false;
+ },
+
+ 'Query#comment': function () {
+ var query = new Query;
+ 'function'.should.equal(typeof query.comment);
+ query.comment('Lowpass is more fun').should.equal(query);
+ query.options.comment.should.equal('Lowpass is more fun');
+ },
+
+ 'test Query#hint': function () {
+ var query = new Query();
+ query.hint('indexAttributeA', 1, 'indexAttributeB', -1);
+ query.options.hint.should.eql({'indexAttributeA': 1, 'indexAttributeB': -1});
+
+ var query2 = new Query();
+ query2.hint({'indexAttributeA': 1, 'indexAttributeB': -1});
+ query2.options.hint.should.eql({'indexAttributeA': 1, 'indexAttributeB': -1});
+
+ var query3 = new Query();
+ query3.hint('indexAttributeA');
+ query3.options.hint.should.eql({});
+ },
+
+ 'test Query#snapshot': function () {
+ var query = new Query();
+ query.snapshot(true);
+ query.options.snapshot.should.be.true;
+ },
+
+ 'test Query#batchSize': function () {
+ var query = new Query();
+ query.batchSize(10);
+ query.options.batchSize.should.equal(10);
+ },
+
+ 'empty updates are not run': function () {
+ var q = new Query;
+ ;(!!q._castUpdate({})).should.be.false;
+ }
+
+ // TODO
+// 'test Query#min': function () {
+// var query = new Query();
+// query.min(10);
+// query.options.min.should.equal(10);
+// },
+//
+ //TODO
+// 'test Query#max': function () {
+// var query = new Query();
+// query.max(100);
+// query.options.max.should.equal(100);
+// },
+
+ // TODO
+// 'test Query#explain': function () {
+// }
+};
diff --git a/node_modules/mongoose/test/schema.onthefly.test.js b/node_modules/mongoose/test/schema.onthefly.test.js
new file mode 100644
index 0000000..d038d78
--- /dev/null
+++ b/node_modules/mongoose/test/schema.onthefly.test.js
@@ -0,0 +1,105 @@
+var start = require('./common')
+ , should = require('should')
+ , mongoose = start.mongoose
+ , random = require('../lib/utils').random
+ , Schema = mongoose.Schema
+ , ObjectId = Schema.ObjectId;
+
+/**
+ * Setup.
+ */
+
+var DecoratedSchema = new Schema({
+ title : String
+});
+
+mongoose.model('Decorated', DecoratedSchema);
+
+var collection = 'decorated_' + random();
+
+module.exports = {
+ 'setting on the fly schemas should cache the type schema and cast values appropriately': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated();
+ post.set('adhoc', '9', Number);
+ post.get('adhoc').valueOf().should.eql(9);
+ db.close();
+ },
+
+ 'on the fly schemas should be local to the particular document': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var postOne = new Decorated();
+ postOne.set('adhoc', '9', Number);
+ postOne._path('adhoc').should.not.equal(undefined);
+
+ var postTwo = new Decorated();
+ postTwo._path('title').should.not.equal(undefined);
+ should.strictEqual(undefined, postTwo._path('adhoc'));
+ db.close();
+ },
+
+ 'querying a document that had an on the fly schema should work': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated({title: 'AD HOC'});
+ // Interpret adhoc as a Number
+ post.set('adhoc', '9', Number);
+ post.get('adhoc').valueOf().should.eql(9);
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ Decorated.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ found.get('adhoc').should.eql(9);
+ // Interpret adhoc as a String instead of a Number now
+ found.get('adhoc', String).should.eql('9');
+ found.get('adhoc').should.eql('9');
+ });
+ });
+ },
+
+ 'on the fly Embedded Array schemas should cast properly': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated();
+ post.set('moderators', [{name: 'alex trebek'}], [new Schema({name: String})]);
+ post.get('moderators')[0].name.should.eql('alex trebek');
+ db.close();
+ },
+
+ 'on the fly Embedded Array schemas should get from a fresh queried document properly': function () {
+ var db = start()
+ , Decorated = db.model('Decorated', collection);
+
+ var post = new Decorated()
+ , ModeratorSchema = new Schema({name: String, ranking: Number});
+ post.set('moderators', [{name: 'alex trebek', ranking: '1'}], [ModeratorSchema]);
+ post.get('moderators')[0].name.should.eql('alex trebek');
+ post.save( function (err) {
+ should.strictEqual(null, err);
+ Decorated.findById(post.id, function (err, found) {
+ db.close();
+ should.strictEqual(null, err);
+ var rankingPreCast = found.get('moderators')[0].ranking;
+ rankingPreCast.should.eql(1);
+ should.strictEqual(undefined, rankingPreCast.increment);
+ var rankingPostCast = found.get('moderators', [ModeratorSchema])[0].ranking;
+ rankingPostCast.valueOf().should.equal(1);
+ rankingPostCast.increment.should.not.equal(undefined);
+
+ var NewModeratorSchema = new Schema({ name: String, ranking: String});
+ rankingPostCast = found.get('moderators', [NewModeratorSchema])[0].ranking;
+ rankingPostCast.should.equal('1');
+ });
+ });
+ },
+ 'should support on the fly nested documents': function () {
+ // TODO
+ }
+};
diff --git a/node_modules/mongoose/test/schema.test.js b/node_modules/mongoose/test/schema.test.js
new file mode 100644
index 0000000..93a3545
--- /dev/null
+++ b/node_modules/mongoose/test/schema.test.js
@@ -0,0 +1,978 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , should = require('should')
+ , 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
+ , MongooseNumber = mongoose.Types.Number
+ , 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.schema = new Schema({
+ test : String
+});
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test different schema types support': 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
+ });
+
+ Ferret.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('owner').should.be.an.instanceof(SchemaTypes.ObjectId);
+ Ferret.path('fur').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('color').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('age').should.be.an.instanceof(SchemaTypes.Number);
+ Ferret.path('checkins').should.be.an.instanceof(SchemaTypes.DocumentArray);
+ Ferret.path('friends').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('likes').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('alive').should.be.an.instanceof(SchemaTypes.Boolean);
+ Ferret.path('extra').should.be.an.instanceof(SchemaTypes.Mixed);
+
+ should.strictEqual(Ferret.path('unexistent'), undefined);
+
+ Checkin.path('date').should.be.an.instanceof(SchemaTypes.Date);
+ },
+
+ 'dot notation support for accessing paths': 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
+ }
+ });
+
+ Person.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Person.path('raccoons').should.be.an.instanceof(SchemaTypes.DocumentArray);
+ Person.path('location.city').should.be.an.instanceof(SchemaTypes.String);
+ Person.path('location.state').should.be.an.instanceof(SchemaTypes.String);
+
+ should.strictEqual(Person.path('location.unexistent'), undefined);
+ },
+
+ 'nested paths more than 2 levels deep': function () {
+ var Nested = new Schema({
+ first: {
+ second: {
+ third: String
+ }
+ }
+ });
+ Nested.path('first.second.third').should.be.an.instanceof(SchemaTypes.String);
+ },
+
+ 'test 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(){
+ this.a.should.eql('b');
+ return '3';
+ }}
+ });
+
+ Test.path('simple').defaultValue.should.eql('a');
+ Test.path('callback').defaultValue.should.be.a('function');
+
+ Test.path('simple').getDefault().should.eql('a');
+ (+Test.path('callback').getDefault({ a: 'b' })).should.eql(3);
+ Test.path('array').defaultValue.should.be.a('function');
+ Test.path('array').getDefault(new TestDocument)[3].should.eql(4);
+ Test.path('arrayX').getDefault(new TestDocument)[0].should.eql(9);
+ Test.path('arrayFn').defaultValue.should.be.a('function');
+ Test.path('arrayFn').getDefault(new TestDocument).should.be.an.instanceof(MongooseArray);
+ },
+
+ 'test Mixed defaults can be empty arrays': function () {
+ var Test = new Schema({
+ mixed1 : { type: Mixed, default: [] }
+ , mixed2 : { type: Mixed, default: Array }
+ });
+
+ Test.path('mixed1').getDefault().should.be.an.instanceof(Array);
+ Test.path('mixed1').getDefault().length.should.be.eql(0);
+ Test.path('mixed2').getDefault().should.be.an.instanceof(Array);
+ Test.path('mixed2').getDefault().length.should.be.eql(0);
+ },
+
+ 'test string required validation': function(){
+ var Test = new Schema({
+ simple: String
+ });
+
+ Test.path('simple').required(true);
+ Test.path('simple').validators.should.have.length(1);
+
+ Test.path('simple').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('woot', function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test string enum validation': function(){
+ var Test = new Schema({
+ complex: { type: String, enum: ['a', 'b', undefined, 'c', null] }
+ });
+
+ Test.path('complex').should.be.an.instanceof(SchemaTypes.String);
+ Test.path('complex').enumValues.should.eql(['a', 'b', 'c', null]);
+ Test.path('complex').validators.should.have.length(1);
+
+ Test.path('complex').enum('d', 'e');
+
+ Test.path('complex').enumValues.should.eql(['a', 'b', 'c', null, 'd', 'e']);
+
+ Test.path('complex').doValidate('x', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('complex').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('complex').doValidate(null, function(err){
+ should.strictEqual(null, err);
+ });
+
+ Test.path('complex').doValidate('da', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test string regular expression validation': function(){
+ var Test = new Schema({
+ simple: { type: String, match: /[a-z]/ }
+ });
+
+ Test.path('simple').validators.should.have.length(1);
+
+ Test.path('simple').doValidate('az', function(err){
+ should.strictEqual(err, null);
+ });
+
+ Test.path('simple').match(/[0-9]/);
+ Test.path('simple').validators.should.have.length(2);
+
+ Test.path('simple').doValidate('12', function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Test.path('simple').doValidate('a12', function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test string casting': function(){
+ var Tobi = new Schema({
+ nickname: String
+ });
+
+ function Test(){};
+ Test.prototype.toString = function(){
+ return 'woot';
+ };
+
+ // test Number -> String cast
+ Tobi.path('nickname').cast(0).should.be.a('string');
+ Tobi.path('nickname').cast(0).should.eql('0');
+
+ // test any object that implements toString
+ Tobi.path('nickname').cast(new Test()).should.be.a('string');
+ Tobi.path('nickname').cast(new Test()).should.eql('woot');
+ },
+
+ 'test number minimums and maximums validation': function(){
+ var Tobi = new Schema({
+ friends: { type: Number, max: 15, min: 5 }
+ });
+
+ Tobi.path('friends').validators.should.have.length(2);
+
+ Tobi.path('friends').doValidate(10, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Tobi.path('friends').doValidate(100, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Tobi.path('friends').doValidate(1, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ // null is allowed
+ Tobi.path('friends').doValidate(null, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test number required validation': function(){
+ var Edwald = new Schema({
+ friends: { type: Number, required: true }
+ });
+
+ Edwald.path('friends').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Edwald.path('friends').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Edwald.path('friends').doValidate(0, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test number casting': function(){
+ var Tobi = new Schema({
+ age: Number
+ });
+
+ // test String -> Number cast
+ Tobi.path('age').cast('0').should.be.an.instanceof(MongooseNumber);
+ (+Tobi.path('age').cast('0')).should.eql(0);
+
+ Tobi.path('age').cast(0).should.be.an.instanceof(MongooseNumber);
+ (+Tobi.path('age').cast(0)).should.eql(0);
+ },
+
+ 'test date required validation': function(){
+ var Loki = new Schema({
+ birth_date: { type: Date, required: true }
+ });
+
+ Loki.path('birth_date').doValidate(null, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('birth_date').doValidate(undefined, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('birth_date').doValidate(new Date(), function (err) {
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test date casting': function(){
+ var Loki = new Schema({
+ birth_date: { type: Date }
+ });
+
+ Loki.path('birth_date').cast(1294525628301).should.be.an.instanceof(Date);
+ Loki.path('birth_date').cast('8/24/2000').should.be.an.instanceof(Date);
+ Loki.path('birth_date').cast(new Date).should.be.an.instanceof(Date);
+ },
+
+ 'test object id required validator': function(){
+ var Loki = new Schema({
+ owner: { type: ObjectId, required: true }
+ });
+
+ Loki.path('owner').doValidate(new DocumentObjectId(), function(err){
+ should.strictEqual(err, null);
+ });
+
+ Loki.path('owner').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('owner').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test object id casting': function(){
+ var Loki = new Schema({
+ owner: { type: ObjectId }
+ });
+
+ var doc = new TestDocument()
+ , id = doc._id.toString();
+
+ Loki.path('owner').cast('4c54f3453e688c000000001a')
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(new DocumentObjectId())
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(doc)
+ .should.be.an.instanceof(DocumentObjectId);
+
+ Loki.path('owner').cast(doc).toString().should.eql(id);
+ },
+
+ 'test array required validation': function(){
+ var Loki = new Schema({
+ likes: { type: Array, required: true }
+ });
+
+ Loki.path('likes').doValidate(null, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('likes').doValidate(undefined, function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Loki.path('likes').doValidate([], function (err) {
+ err.should.be.an.instanceof(ValidatorError);
+ });
+ },
+
+ 'test array casting': 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]);
+
+ oids[0].should.be.an.instanceof(DocumentObjectId);
+ oids[1].should.be.an.instanceof(DocumentObjectId);
+
+ var dates = Loki.path('dates').cast(['8/24/2010', 1294541504958]);
+
+ dates[0].should.be.an.instanceof(Date);
+ dates[1].should.be.an.instanceof(Date);
+
+ var numbers = Loki.path('numbers').cast([152, '31']);
+
+ numbers[0].should.be.a('number');
+ numbers[1].should.be.a('number');
+
+ var strings = Loki.path('strings').cast(['test', 123]);
+
+ strings[0].should.be.a('string');
+ strings[0].should.eql('test');
+
+ strings[1].should.be.a('string');
+ strings[1].should.eql('123');
+
+ var buffers = Loki.path('buffers').cast(['\0\0\0', new Buffer("abc")]);
+
+ buffers[0].should.be.an.instanceof(Buffer);
+ buffers[1].should.be.an.instanceof(Buffer);
+
+ var nocasts = Loki.path('nocast').cast(['test', 123]);
+
+ nocasts[0].should.be.a('string');
+ nocasts[0].should.eql('test');
+
+ nocasts[1].should.be.a('number');
+ nocasts[1].should.eql(123);
+
+ var mixed = Loki.path('mixed').cast(['test', 123, '123', {}, new Date, new DocumentObjectId]);
+
+ mixed[0].should.be.a('string');
+ mixed[1].should.be.a('number');
+ mixed[2].should.be.a('string');
+ mixed[3].should.be.a('object');
+ mixed[4].should.be.an.instanceof(Date);
+ mixed[5].should.be.an.instanceof(DocumentObjectId);
+ },
+
+ 'test boolean required validator': function(){
+ var Animal = new Schema({
+ isFerret: { type: Boolean, required: true }
+ });
+
+ Animal.path('isFerret').doValidate(null, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Animal.path('isFerret').doValidate(undefined, function(err){
+ err.should.be.an.instanceof(ValidatorError);
+ });
+
+ Animal.path('isFerret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Animal.path('isFerret').doValidate(false, function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'test boolean casting': function(){
+ var Animal = new Schema({
+ isFerret: { type: Boolean, required: true }
+ });
+
+ should.strictEqual(Animal.path('isFerret').cast(null), null);
+ Animal.path('isFerret').cast(undefined).should.be.false;
+ Animal.path('isFerret').cast(false).should.be.false;
+ Animal.path('isFerret').cast(0).should.be.false;
+ Animal.path('isFerret').cast('0').should.be.false;
+ Animal.path('isFerret').cast({}).should.be.true;
+ Animal.path('isFerret').cast(true).should.be.true;
+ Animal.path('isFerret').cast(1).should.be.true;
+ Animal.path('isFerret').cast('1').should.be.true;
+ },
+
+ 'test async multiple validators': function(beforeExit){
+ var executed = 0;
+
+ function validator (value, fn) {
+ setTimeout(function(){
+ executed++;
+ fn(value === true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: {
+ type: Boolean,
+ validate: [
+ {
+ 'validator': validator,
+ 'msg': 'validator1'
+ },
+ {
+ 'validator': validator,
+ 'msg': 'validator2'
+ },
+ ],
+ }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ beforeExit(function(){
+ executed.should.eql(2);
+ });
+ },
+
+ 'test async validators': function(beforeExit){
+ var executed = 0;
+
+ function validator (value, fn) {
+ setTimeout(function(){
+ executed++;
+ fn(value === true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: { type: Boolean, validate: validator }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ });
+
+ Animal.path('ferret').doValidate(false, function(err){
+ err.should.be.an.instanceof(Error);
+ });
+
+ beforeExit(function(){
+ executed.should.eql(2);
+ });
+ },
+
+ 'test async validators scope': function(beforeExit){
+ var executed = false;
+
+ function validator (value, fn) {
+ this.a.should.eql('b');
+
+ setTimeout(function(){
+ executed = true;
+ fn(true);
+ }, 50);
+ };
+
+ var Animal = new Schema({
+ ferret: { type: Boolean, validate: validator }
+ });
+
+ Animal.path('ferret').doValidate(true, function(err){
+ should.strictEqual(err, null);
+ }, { a: 'b' });
+
+ beforeExit(function(){
+ executed.should.be.true;
+ });
+ },
+
+ 'test declaring new methods': function(){
+ var a = new Schema();
+ a.method('test', function(){});
+ a.method({
+ a: function(){}
+ , b: function(){}
+ });
+
+ Object.keys(a.methods).should.have.length(3);
+ },
+
+ 'test declaring new statics': function(){
+ var a = new Schema();
+ a.static('test', function(){});
+ a.static({
+ a: function(){}
+ , b: function(){}
+ , c: function(){}
+ });
+
+ Object.keys(a.statics).should.have.length(4);
+ },
+
+ 'test setter(s)': function(){
+ function lowercase (v) {
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: lowercase }
+ });
+
+ Tobi.path('name').applySetters('WOOT').should.eql('woot');
+ Tobi.path('name').setters.should.have.length(1);
+
+ Tobi.path('name').set(function(v){
+ return v + 'WOOT';
+ });
+
+ Tobi.path('name').applySetters('WOOT').should.eql('wootwoot');
+ Tobi.path('name').setters.should.have.length(2);
+ },
+
+ 'test setters scope': function(){
+ function lowercase (v, self) {
+ this.a.should.eql('b');
+ self.path.should.eql('name');
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: lowercase }
+ });
+
+ Tobi.path('name').applySetters('WHAT', { a: 'b' }).should.eql('what');
+ },
+
+ 'test string built-in setter `lowercase`': function () {
+ var Tobi = new Schema({
+ name: { type: String, lowercase: true }
+ });
+
+ Tobi.path('name').applySetters('WHAT').should.eql('what');
+ },
+
+ 'test string built-in setter `uppercase`': function () {
+ var Tobi = new Schema({
+ name: { type: String, uppercase: true }
+ });
+
+ Tobi.path('name').applySetters('what').should.eql('WHAT');
+ },
+
+ 'test string built-in setter `trim`': function () {
+ var Tobi = new Schema({
+ name: { type: String, uppercase: true, trim: true }
+ });
+
+ Tobi.path('name').applySetters(' what ').should.eql('WHAT');
+ },
+
+ 'test getter(s)': function(){
+ function woot (v) {
+ return v + ' woot';
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: woot }
+ });
+
+ Tobi.path('name').getters.should.have.length(1);
+ Tobi.path('name').applyGetters('test').should.eql('test woot');
+ },
+
+ 'test getters scope': function(){
+ function woot (v, self) {
+ this.a.should.eql('b');
+ self.path.should.eql('name');
+ return v.toLowerCase();
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: woot }
+ });
+
+ Tobi.path('name').applyGetters('YEP', { a: 'b' }).should.eql('yep');
+ },
+
+ 'test setters casting': function(){
+ function last (v) {
+ v.should.be.a('string');
+ v.should.eql('0');
+ return 'last';
+ };
+
+ function first (v) {
+ return 0;
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, set: last }
+ });
+
+ Tobi.path('name').set(first);
+ Tobi.path('name').applySetters('woot').should.eql('last');
+ },
+
+ 'test getters casting': function(){
+ function last (v) {
+ v.should.be.a('string');
+ v.should.eql('0');
+ return 'last';
+ };
+
+ function first (v) {
+ return 0;
+ };
+
+ var Tobi = new Schema({
+ name: { type: String, get: last }
+ });
+
+ Tobi.path('name').get(first);
+ Tobi.path('name').applyGetters('woot').should.eql('last');
+ },
+
+ 'test hooks registration': function(){
+ var Tobi = new Schema();
+
+ Tobi.pre('save', function(){});
+ Tobi.callQueue.should.have.length(1);
+
+ Tobi.post('save', function(){});
+ Tobi.callQueue.should.have.length(2);
+
+ Tobi.pre('save', function(){});
+ Tobi.callQueue.should.have.length(3);
+ },
+
+ 'test applying setters when none have been defined': function(){
+ var Tobi = new Schema({
+ name: String
+ });
+
+ Tobi.path('name').applySetters('woot').should.eql('woot');
+ },
+
+ 'test applying getters when none have been defined': function(){
+ var Tobi = new Schema({
+ name: String
+ });
+
+ Tobi.path('name').applyGetters('woot').should.eql('woot');
+ },
+
+ 'test defining an index': function(){
+ var Tobi = new Schema({
+ name: { type: String, index: true }
+ });
+
+ Tobi.path('name')._index.should.be.true;
+ Tobi.path('name').index({ unique: true });
+ Tobi.path('name')._index.should.eql({ unique: true });
+ Tobi.path('name').unique(false);
+ Tobi.path('name')._index.should.eql({ unique: false});
+
+ var T1 = new Schema({
+ name: { type: String, sparse: true }
+ });
+ T1.path('name')._index.should.eql({ sparse: true });
+
+ var T2 = new Schema({
+ name: { type: String, unique: true }
+ });
+ T2.path('name')._index.should.eql({ unique: true });
+
+ var T3 = new Schema({
+ name: { type: String, sparse: true, unique: true }
+ });
+ T3.path('name')._index.should.eql({ sparse: true, unique: true });
+
+ var T4 = new Schema({
+ name: { type: String, unique: true, sparse: true }
+ });
+ var i = T4.path('name')._index;
+ i.unique.should.be.true;
+ i.sparse.should.be.true;
+
+ var T5 = new Schema({
+ name: { type: String, index: { sparse: true, unique: true } }
+ });
+ var i = T5.path('name')._index;
+ i.unique.should.be.true;
+ i.sparse.should.be.true;
+ },
+
+ 'test defining compound indexes': function(){
+ var Tobi = new Schema({
+ name: { type: String, index: true }
+ , last: { type: Number, sparse: true }
+ });
+
+ Tobi.index({ firstname: 1, last: 1 }, { unique: true });
+
+ Tobi.indexes.should.eql([
+ [{ name: 1 }, {}]
+ , [{ last: 1 }, { sparse: true }]
+ , [{ firstname: 1, last: 1}, {unique: true}]
+ ]);
+ },
+
+ 'test plugins': function (beforeExit) {
+ var Tobi = new Schema()
+ , called = false;
+
+ Tobi.plugin(function(schema){
+ schema.should.equal(Tobi);
+ called = true;
+ });
+
+ beforeExit(function () {
+ called.should.be.true;
+ });
+ },
+
+ 'test that default options are set': function () {
+ var Tobi = new Schema();
+
+ Tobi.options.should.be.a('object');
+ Tobi.options.safe.should.be.true;
+ },
+
+ 'test setting options': function () {
+ var Tobi = new Schema({}, { collection: 'users' });
+
+ Tobi.set('a', 'b');
+ Tobi.set('safe', false);
+ Tobi.options.collection.should.eql('users');
+
+ Tobi.options.a.should.eql('b');
+ Tobi.options.safe.should.be.false;
+ },
+
+ 'test declaring virtual attributes': 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]);
+ });
+
+ Contact.virtualpath('fullName').should.be.an.instanceof(VirtualType);
+ },
+
+ 'test GH-298 - The default creation of a virtual `id` should be muted when someone defines their own `id` attribute': function () {
+ new Schema({ id: String });
+ },
+
+ 'allow disabling the auto .id virtual': function () {
+ var schema = new Schema({ name: String }, { noVirtualId: true });
+ should.strictEqual(undefined, schema.virtuals.id);
+ },
+
+ 'selected option': function () {
+ var s = new Schema({ thought: { type: String, select: false }});
+ s.path('thought').selected.should.be.false;
+
+ var a = new Schema({ thought: { type: String, select: true }});
+ a.path('thought').selected.should.be.true;
+ },
+
+ 'schema creation works with objects from other contexts': 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);
+ Ferret.path('nest.sub').should.be.an.instanceof(SchemaTypes.Mixed);
+ Ferret.path('name').should.be.an.instanceof(SchemaTypes.String);
+ Ferret.path('arr1').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('arr2').should.be.an.instanceof(SchemaTypes.Array);
+ Ferret.path('date').should.be.an.instanceof(SchemaTypes.Date);
+ Ferret.path('num').should.be.an.instanceof(SchemaTypes.Number);
+ Ferret.path('bool').should.be.an.instanceof(SchemaTypes.Boolean);
+ },
+
+ 'schema string casts undefined to "undefined"': function () {
+ 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();
+ should.equal(err && err.message, 'Cast to string failed for value "undefined"');
+ })
+ },
+
+ 'array of object literal missing a `type` is interpreted as Mixed': function () {
+ var s = new Schema({
+ arr: [
+ { something: { type: String } }
+ ]
+ });
+ },
+
+ 'helpful schema debugging msg': function () {
+ var err;
+ try {
+ new Schema({ name: { first: null } })
+ } catch (e) {
+ err = e;
+ }
+ err.message.should.equal('Invalid value for schema path `name.first`')
+ try {
+ new Schema({ age: undefined })
+ } catch (e) {
+ err = e;
+ }
+ err.message.should.equal('Invalid value for schema path `age`')
+ },
+
+ 'add() does not polute existing paths': function () {
+ var o = { name: String }
+ var s = new Schema(o);
+ s.add({ age: Number }, 'name.');
+ ;('age' in o.name).should.be.false;
+ },
+
+ // gh-700
+ 'nested schemas should throw': function () {
+ var a = new Schema({ title: String })
+ , err
+
+ try {
+ new Schema({ blah: Boolean, a: a });
+ } catch (err_) {
+ err = err_;
+ }
+
+ should.exist(err);
+ ;/Did you try nesting Schemas/.test(err.message).should.be.true;
+ },
+
+ 'non-function etters throw': function () {
+ var schema = new Schema({ fun: String });
+ var g, s;
+
+ try {
+ schema.path('fun').get(true);
+ } catch (err_) {
+ g = err_;
+ }
+
+ should.exist(g);
+ g.message.should.equal('A getter must be a function.');
+
+ try {
+ schema.path('fun').set(4);
+ } catch (err_) {
+ s = err_;
+ }
+
+ should.exist(s);
+ s.message.should.equal('A setter must be a function.');
+ }
+
+};
diff --git a/node_modules/mongoose/test/shard.test.js b/node_modules/mongoose/test/shard.test.js
new file mode 100644
index 0000000..2df04c3
--- /dev/null
+++ b/node_modules/mongoose/test/shard.test.js
@@ -0,0 +1,216 @@
+
+var start = require('./common')
+ , should = require('should')
+ , random = require('../lib/utils').random
+ , mongoose = start.mongoose
+ , Mongoose = mongoose.Mongoose
+ , Schema = mongoose.Schema;
+
+var uri = process.env.MONGOOSE_SHARD_TEST_URI;
+
+if (!uri) {
+ console.log('\033[30m', '\n', 'You\'re not testing shards!'
+ , '\n', 'Please set the MONGOOSE_SHARD_TEST_URI env variable.', '\n'
+ , 'e.g: `mongodb://localhost:27017/database', '\n'
+ , 'Sharding must already be enabled on your database'
+ , '\033[39m');
+
+ exports.r = function expressoHack(){}
+ return;
+}
+
+var schema = new Schema({
+ name: String
+ , age: Number
+ , likes: [String]
+}, { shardkey: { name: 1, age: 1 }});
+
+var collection = 'shardperson_' + random();
+mongoose.model('ShardPerson', schema, collection);
+
+var db = start({ uri: uri });
+db.on('open', function () {
+
+ // set up a sharded test collection
+ var P = db.model('ShardPerson', collection);
+
+ var cmd = {};
+ cmd.shardcollection = db.name + '.' + collection;
+ cmd.key = P.schema.options.shardkey;
+
+ P.db.db.executeDbAdminCommand(cmd,function (err, res) {
+ db.close();
+
+ if (err) throw err;
+
+ if (!(res && res.documents && res.documents[0] && res.documents[0].ok)) {
+ throw new Error('could not shard test collection ' + collection);
+ }
+
+ // assign exports to tell expresso to begin
+ Object.keys(tests).forEach(function (test) {
+ exports[test] = tests[test];
+ });
+
+ });
+});
+
+var tests = {
+
+ 'can read and write to a shard': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ P.create({ name: 'ryu', age: 25, likes: ['street fighting']}, function (err, ryu) {
+ should.strictEqual(null, err);
+ P.findById(ryu._id, function (err, doc) {
+ db.close();
+ should.strictEqual(null, err);
+ doc.id.should.equal(ryu.id);
+ });
+ });
+ },
+
+ 'save() and remove() works with shard keys transparently': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ var zangief = new P({ name: 'Zangief', age: 33 });
+ zangief.save(function (err) {
+ should.strictEqual(null, err);
+
+ zangief._shardval.name.should.equal('Zangief');
+ zangief._shardval.age.should.equal(33);
+
+ P.findById(zangief._id, function (err, zang) {
+ should.strictEqual(null, err);
+
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+
+ zang.likes = ['spinning', 'laughing'];
+ zang.save(function (err) {
+ should.strictEqual(null, err);
+
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+
+ zang.likes.addToSet('winning');
+ zang.save(function (err) {
+ should.strictEqual(null, err);
+ zang._shardval.name.should.equal('Zangief');
+ zang._shardval.age.should.equal(33);
+ zang.remove(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'inserting to a sharded collection without the full shard key fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ var pending = 6;
+
+ P.create({ name: 'ryu', likes: ['street fighting']}, function (err, ryu) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ likes: ['street fighting']}, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ name: 'ryu' }, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ age: 49 }, function (err, ryu) { should.exist(err);
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ P.create({ likes: ['street fighting'], age: 8 }, function (err, ryu) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ var p = new P;
+ p.save(function (err) {
+ --pending || db.close();
+ should.exist(err);
+ err.message.should.equal('tried to insert object with no valid shard key');
+ });
+
+ },
+
+ 'updating a sharded collection without the full shard key fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+
+ P.create({ name: 'ken', age: 27 }, function (err, ken) {
+ should.strictEqual(null, err);
+
+ P.update({ _id: ken._id }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+ "right object doesn't have full shard key".should.equal(err.message);
+
+ P.update({ _id: ken._id, name: 'ken' }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+
+ P.update({ _id: ken._id, age: 27 }, { likes: ['kicking', 'punching'] }, function (err) {
+ should.exist(err);
+
+ P.update({ age: 27 }, { likes: ['kicking', 'punching'] }, function (err) {
+ db.close();
+ should.exist(err);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'updating shard key values fails': function () {
+ var db = start({ uri: uri })
+ var P = db.model('ShardPerson', collection);
+ P.create({ name: 'chun li', age: 19, likes: ['street fighting']}, function (err, chunli) {
+ should.strictEqual(null, err);
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ chunli.age = 20;
+ chunli.save(function (err) {
+ /^Can't modify shard key's value field/.test(err.message).should.be.true;
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ P.findById(chunli._id, function (err, chunli) {
+ should.strictEqual(null, err);
+
+ chunli._shardval.name.should.equal('chun li');
+ chunli._shardval.age.should.equal(19);
+
+ chunli.name='chuuuun liiiii';
+ chunli.save(function (err) {
+ db.close();
+ /^Can't modify shard key's value field/.test(err.message).should.be.true;
+ });
+ });
+ });
+ });
+ }
+}
diff --git a/node_modules/mongoose/test/types.array.test.js b/node_modules/mongoose/test/types.array.test.js
new file mode 100644
index 0000000..8a37399
--- /dev/null
+++ b/node_modules/mongoose/test/types.array.test.js
@@ -0,0 +1,551 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = require('./common').mongoose
+ , Schema = mongoose.Schema
+ , random = require('../lib/utils').random
+ , MongooseArray = mongoose.Types.Array;
+
+var User = new Schema({
+ name: String
+ , pets: [Schema.ObjectId]
+});
+
+mongoose.model('User', User);
+
+var Pet = new Schema({
+ name: String
+});
+
+mongoose.model('Pet', Pet);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose array behaves and quacks like an array': function(){
+ var a = new MongooseArray;
+
+ a.should.be.an.instanceof(Array);
+ a.should.be.an.instanceof(MongooseArray);
+ Array.isArray(a).should.be.true;
+ ;(a._atomics.constructor).should.eql(Object);
+
+ },
+
+ 'doAtomics does not throw': function () {
+ var b = new MongooseArray([12,3,4,5]).filter(Boolean);
+ var threw = false;
+
+ try {
+ b.doAtomics
+ } catch (_) {
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ var a = new MongooseArray([67,8]).filter(Boolean);
+ try {
+ a.push(3,4);
+ } catch (_) {
+ console.error(_);
+ threw = true;
+ }
+
+ threw.should.be.false;
+
+ },
+
+ 'test indexOf()': function(){
+ var db = start()
+ , User = db.model('User', 'users_' + random())
+ , Pet = db.model('Pet', 'pets' + random());
+
+ var tj = new User({ name: 'tj' })
+ , tobi = new Pet({ name: 'tobi' })
+ , loki = new Pet({ name: 'loki' })
+ , jane = new Pet({ name: 'jane' })
+ , pets = [];
+
+ tj.pets.push(tobi);
+ tj.pets.push(loki);
+ tj.pets.push(jane);
+
+ var pending = 3;
+
+ ;[tobi, loki, jane].forEach(function(pet){
+ pet.save(function(){
+ --pending || done();
+ });
+ });
+
+ function done() {
+ Pet.find({}, function(err, pets){
+ tj.save(function(err){
+ User.findOne({ name: 'tj' }, function(err, user){
+ db.close();
+ should.equal(null, err, 'error in callback');
+ user.pets.should.have.length(3);
+ user.pets.indexOf(tobi.id).should.equal(0);
+ user.pets.indexOf(loki.id).should.equal(1);
+ user.pets.indexOf(jane.id).should.equal(2);
+ user.pets.indexOf(tobi._id).should.equal(0);
+ user.pets.indexOf(loki._id).should.equal(1);
+ user.pets.indexOf(jane._id).should.equal(2);
+ });
+ });
+ });
+ }
+ },
+
+ 'test #splice() with numbers': function () {
+ var collection = 'splicetest-number' + random();
+ var db = start()
+ , schema = new Schema({ numbers: Array })
+ , A = db.model('splicetestNumber', schema, collection);
+
+ var a = new A({ numbers: [4,5,6,7] });
+ a.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+ var removed = doc.numbers.splice(1, 1);
+ removed.should.eql([5]);
+ doc.numbers.toObject().should.eql([4,6,7]);
+ doc.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+ doc.numbers.toObject().should.eql([4,6,7]);
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'test #splice() on embedded docs': function () {
+ var collection = 'splicetest-embeddeddocs' + random();
+ var db = start()
+ , schema = new Schema({ types: [new Schema({ type: String }) ]})
+ , A = db.model('splicetestEmbeddedDoc', schema, collection);
+
+ var a = new A({ types: [{type:'bird'},{type:'boy'},{type:'frog'},{type:'cloud'}] });
+ a.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+
+ var removed = doc.types.splice(1, 1);
+ removed.length.should.eql(1);
+ removed[0].type.should.eql('boy');
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('bird');
+ obj[1].type.should.eql('frog');
+ obj[2].type.should.eql('cloud');
+
+ doc.save(function (err) {
+ should.equal(null, err, 'could not save splice test');
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err, 'error finding splice doc');
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('bird');
+ obj[1].type.should.eql('frog');
+ obj[2].type.should.eql('cloud');
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ '#unshift': function () {
+ var db = start()
+ , schema = new Schema({
+ types: [new Schema({ type: String })]
+ , nums: [Number]
+ , strs: [String]
+ })
+ , A = db.model('unshift', schema, 'unshift'+random());
+
+ var a = new A({
+ types: [{type:'bird'},{type:'boy'},{type:'frog'},{type:'cloud'}]
+ , nums: [1,2,3]
+ , strs: 'one two three'.split(' ')
+ });
+
+ a.save(function (err) {
+ should.equal(null, err);
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err);
+
+ var tlen = doc.types.unshift({type:'tree'});
+ var nlen = doc.nums.unshift(0);
+ var slen = doc.strs.unshift('zero');
+
+ tlen.should.equal(5);
+ nlen.should.equal(4);
+ slen.should.equal(4);
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('tree');
+ obj[1].type.should.eql('bird');
+ obj[2].type.should.eql('boy');
+ obj[3].type.should.eql('frog');
+ obj[4].type.should.eql('cloud');
+
+ obj = doc.nums.toObject();
+ obj[0].valueOf().should.equal(0);
+ obj[1].valueOf().should.equal(1);
+ obj[2].valueOf().should.equal(2);
+ obj[3].valueOf().should.equal(3);
+
+ obj = doc.strs.toObject();
+ obj[0].should.equal('zero');
+ obj[1].should.equal('one');
+ obj[2].should.equal('two');
+ obj[3].should.equal('three');
+
+ doc.save(function (err) {
+ should.equal(null, err);
+ A.findById(a._id, function (err, doc) {
+ should.equal(null, err);
+
+ var obj = doc.types.toObject();
+ obj[0].type.should.eql('tree');
+ obj[1].type.should.eql('bird');
+ obj[2].type.should.eql('boy');
+ obj[3].type.should.eql('frog');
+ obj[4].type.should.eql('cloud');
+
+ obj = doc.nums.toObject();
+ obj[0].valueOf().should.equal(0);
+ obj[1].valueOf().should.equal(1);
+ obj[2].valueOf().should.equal(2);
+ obj[3].valueOf().should.equal(3);
+
+ obj = doc.strs.toObject();
+ obj[0].should.equal('zero');
+ obj[1].should.equal('one');
+ obj[2].should.equal('two');
+ obj[3].should.equal('three');
+
+ A.collection.drop(function (err) {
+ db.close();
+ should.strictEqual(err, null);
+ });
+ });
+ });
+ });
+ });
+ },
+
+ '#addToSet': function () {
+ var db = start()
+ , e = new Schema({ name: String, arr: [] })
+ , schema = new Schema({
+ num: [Number]
+ , str: [String]
+ , doc: [e]
+ , date: [Date]
+ , id: [Schema.ObjectId]
+ });
+
+ var M = db.model('testAddToSet', schema);
+ var m = new M;
+
+ m.num.push(1,2,3);
+ m.str.push('one','two','tres');
+ m.doc.push({ name: 'Dubstep', arr: [1] }, { name: 'Polka', arr: [{ x: 3 }]});
+
+ var d1 = new Date;
+ var d2 = new Date( +d1 + 60000);
+ var d3 = new Date( +d1 + 30000);
+ var d4 = new Date( +d1 + 20000);
+ var d5 = new Date( +d1 + 90000);
+ var d6 = new Date( +d1 + 10000);
+ m.date.push(d1, d2);
+
+ var id1 = new mongoose.Types.ObjectId;
+ var id2 = new mongoose.Types.ObjectId;
+ var id3 = new mongoose.Types.ObjectId;
+ var id4 = new mongoose.Types.ObjectId;
+ var id5 = new mongoose.Types.ObjectId;
+ var id6 = new mongoose.Types.ObjectId;
+
+ m.id.push(id1, id2);
+
+ m.num.addToSet(3,4,5);
+ m.num.length.should.equal(5);
+ m.str.$addToSet('four', 'five', 'two');
+ m.str.length.should.equal(5);
+ m.id.addToSet(id2, id3);
+ m.id.length.should.equal(3);
+ m.doc.$addToSet(m.doc[0]);
+ m.doc.length.should.equal(2);
+ m.doc.$addToSet({ name: 'Waltz', arr: [1] }, m.doc[0]);
+ m.doc.length.should.equal(3);
+ m.date.length.should.equal(2);
+ m.date.$addToSet(d1);
+ m.date.length.should.equal(2);
+ m.date.addToSet(d3);
+ m.date.length.should.equal(3);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(5);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+
+ m.str.length.should.equal(5);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+
+ m.id.length.should.equal(3);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+
+ m.date.length.should.equal(3);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+
+ m.doc.length.should.equal(3);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+
+ // test single $addToSet
+ m.num.addToSet(3,4,5,6);
+ m.num.length.should.equal(6);
+ m.str.$addToSet('four', 'five', 'two', 'six');
+ m.str.length.should.equal(6);
+ m.id.addToSet(id2, id3, id4);
+ m.id.length.should.equal(4);
+
+ m.date.$addToSet(d1, d3, d4);
+ m.date.length.should.equal(4);
+
+ m.doc.$addToSet(m.doc[0], { name: '8bit' });
+ m.doc.length.should.equal(4);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(6);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+ (~m.num.indexOf(6)).should.be.ok;
+
+ m.str.length.should.equal(6);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+ (~m.str.indexOf('six')).should.be.ok;
+
+ m.id.length.should.equal(4);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+ (~m.id.indexOf(id4)).should.be.ok;
+
+ m.date.length.should.equal(4);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+ (~m.date.indexOf(d4.toString())).should.be.ok;
+
+ m.doc.length.should.equal(4);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+ m.doc.some(function(v){return v.name === '8bit'}).should.be.ok
+
+ // test multiple $addToSet
+ m.num.addToSet(7,8);
+ m.num.length.should.equal(8);
+ m.str.$addToSet('seven', 'eight');
+ m.str.length.should.equal(8);
+ m.id.addToSet(id5, id6);
+ m.id.length.should.equal(6);
+
+ m.date.$addToSet(d5, d6);
+ m.date.length.should.equal(6);
+
+ m.doc.$addToSet(m.doc[1], { name: 'BigBeat' }, { name: 'Funk' });
+ m.doc.length.should.equal(6);
+
+ m.save(function (err) {
+ should.strictEqual(null, err);
+
+ M.findById(m, function (err, m) {
+ db.close();
+ should.strictEqual(null, err);
+
+ m.num.length.should.equal(8);
+ (~m.num.indexOf(1)).should.be.ok;
+ (~m.num.indexOf(2)).should.be.ok;
+ (~m.num.indexOf(3)).should.be.ok;
+ (~m.num.indexOf(4)).should.be.ok;
+ (~m.num.indexOf(5)).should.be.ok;
+ (~m.num.indexOf(6)).should.be.ok;
+ (~m.num.indexOf(7)).should.be.ok;
+ (~m.num.indexOf(8)).should.be.ok;
+
+ m.str.length.should.equal(8);
+ (~m.str.indexOf('one')).should.be.ok;
+ (~m.str.indexOf('two')).should.be.ok;
+ (~m.str.indexOf('tres')).should.be.ok;
+ (~m.str.indexOf('four')).should.be.ok;
+ (~m.str.indexOf('five')).should.be.ok;
+ (~m.str.indexOf('six')).should.be.ok;
+ (~m.str.indexOf('seven')).should.be.ok;
+ (~m.str.indexOf('eight')).should.be.ok;
+
+ m.id.length.should.equal(6);
+ (~m.id.indexOf(id1)).should.be.ok;
+ (~m.id.indexOf(id2)).should.be.ok;
+ (~m.id.indexOf(id3)).should.be.ok;
+ (~m.id.indexOf(id4)).should.be.ok;
+ (~m.id.indexOf(id5)).should.be.ok;
+ (~m.id.indexOf(id6)).should.be.ok;
+
+ m.date.length.should.equal(6);
+ (~m.date.indexOf(d1.toString())).should.be.ok;
+ (~m.date.indexOf(d2.toString())).should.be.ok;
+ (~m.date.indexOf(d3.toString())).should.be.ok;
+ (~m.date.indexOf(d4.toString())).should.be.ok;
+ (~m.date.indexOf(d5.toString())).should.be.ok;
+ (~m.date.indexOf(d6.toString())).should.be.ok;
+
+ m.doc.length.should.equal(6);
+ m.doc.some(function(v){return v.name === 'Waltz'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Dubstep'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Polka'}).should.be.ok
+ m.doc.some(function(v){return v.name === '8bit'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'BigBeat'}).should.be.ok
+ m.doc.some(function(v){return v.name === 'Funk'}).should.be.ok
+ });
+ });
+ });
+ });
+ });
+ });
+ },
+
+ 'setting doc array should adjust path positions': function () {
+ var db = start();
+
+ var D = db.model('subDocPositions', new Schema({
+ em1: [new Schema({ name: String })]
+ }));
+
+ var d = new D({
+ em1: [
+ { name: 'pos0' }
+ , { name: 'pos1' }
+ , { name: 'pos2' }
+ ]
+ });
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ should.strictEqual(null, err);
+
+ var n = d.em1.slice();
+ n[2].name = 'position two';
+ var x = [];
+ x[1] = n[2];
+ x[2] = n[1];
+ d.em1 = x.filter(Boolean);
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ },
+
+ 'paths with similar names should be saved': function () {
+ var db = start();
+
+ var D = db.model('similarPathNames', new Schema({
+ account: {
+ role: String
+ , roles: [String]
+ }
+ , em: [new Schema({ name: String })]
+ }));
+
+ var d = new D({
+ account: { role: 'teacher', roles: ['teacher', 'admin'] }
+ , em: [{ name: 'bob' }]
+ });
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ should.strictEqual(null, err);
+
+ d.account.role = 'president';
+ d.account.roles = ['president', 'janitor'];
+ d.em[0].name = 'memorable';
+ d.em = [{ name: 'frida' }];
+
+ d.save(function (err) {
+ should.strictEqual(null, err);
+ D.findById(d, function (err, d) {
+ db.close();
+ should.strictEqual(null, err);
+ d.account.role.should.equal('president');
+ d.account.roles.length.should.equal(2);
+ d.account.roles[0].should.equal('president');
+ d.account.roles[1].should.equal('janitor');
+ d.em.length.should.equal(1);
+ d.em[0].name.should.equal('frida');
+ });
+ });
+ });
+ });
+ }
+};
diff --git a/node_modules/mongoose/test/types.buffer.test.js b/node_modules/mongoose/test/types.buffer.test.js
new file mode 100644
index 0000000..b9c8d5d
--- /dev/null
+++ b/node_modules/mongoose/test/types.buffer.test.js
@@ -0,0 +1,350 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , should = require('should')
+ , mongoose = require('./common').mongoose
+ , Schema = mongoose.Schema
+ , random = require('../lib/utils').random
+ , MongooseBuffer = mongoose.Types.Buffer;
+
+// can't index Buffer fields yet
+
+function valid (v) {
+ return !v || v.length > 10;
+}
+
+var subBuf = new Schema({
+ name: String
+ , buf: { type: Buffer, validate: [valid, 'valid failed'], required: true }
+});
+
+var UserBuffer = new Schema({
+ name: String
+ , serial: Buffer
+ , array: [Buffer]
+ , required: { type: Buffer, required: true, index: true }
+ , sub: [subBuf]
+});
+
+// Dont put indexed models on the default connection, it
+// breaks index.test.js tests on a "pure" default conn.
+// mongoose.model('UserBuffer', UserBuffer);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose buffer behaves and quacks like an buffer': function(){
+ var a = new MongooseBuffer;
+
+ a.should.be.an.instanceof(Buffer);
+ a.should.be.an.instanceof(MongooseBuffer);
+ Buffer.isBuffer(a).should.be.true;
+
+ var a = new MongooseBuffer([195, 188, 98, 101, 114]);
+ var b = new MongooseBuffer("buffer shtuffs are neat");
+ var c = new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64');
+
+ a.toString('utf8').should.equal('über');
+ b.toString('utf8').should.equal('buffer shtuffs are neat');
+ c.toString('utf8').should.equal('hello world');
+ },
+
+ 'buffer validation': function () {
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var t = new User({
+ name: 'test validation'
+ });
+
+ t.validate(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.required.type.should.equal('required');
+ t.required = 20;
+ t.save(function (err) {
+ err.name.should.eql('CastError');
+ err.type.should.eql('buffer');
+ err.value.should.equal(20);
+ err.message.should.eql('Cast to buffer failed for value "20"');
+ t.required = new Buffer("hello");
+
+ t.sub.push({ name: 'Friday Friday' });
+ t.save(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.buf.type.should.equal('required');
+ t.sub[0].buf = new Buffer("well well");
+ t.save(function (err) {
+ err.message.should.eql('Validation failed');
+ err.errors.buf.type.should.equal('valid failed');
+
+ t.sub[0].buf = new Buffer("well well well");
+ t.validate(function (err) {
+ db.close();
+ should.strictEqual(null, err);
+ });
+ });
+ });
+ });
+ });
+ })
+ },
+
+ 'buffer storage': function(){
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var sampleBuffer = new Buffer([123, 223, 23, 42, 11]);
+
+ var tj = new User({
+ name: 'tj'
+ , serial: sampleBuffer
+ , required: new Buffer(sampleBuffer)
+ });
+
+ tj.save(function (err) {
+ should.equal(null, err);
+ User.find({}, function (err, users) {
+ db.close();
+ should.equal(null, err);
+ users.should.have.length(1);
+ var user = users[0];
+ var base64 = sampleBuffer.toString('base64');
+ should.equal(base64,
+ user.serial.toString('base64'), 'buffer mismatch');
+ should.equal(base64,
+ user.required.toString('base64'), 'buffer mismatch');
+ });
+ });
+ });
+ },
+
+ 'test write markModified': function(){
+ var db = start()
+ , User = db.model('UserBuffer', UserBuffer, 'usersbuffer_' + random());
+
+ User.on('index', function () {
+ var sampleBuffer = new Buffer([123, 223, 23, 42, 11]);
+
+ var tj = new User({
+ name: 'tj'
+ , serial: sampleBuffer
+ , required: sampleBuffer
+ });
+
+ tj.save(function (err) {
+ should.equal(null, err);
+
+ tj.serial.write('aa', 1, 'ascii');
+ tj.isModified('serial').should.be.true;
+
+ tj.save(function (err) {
+ should.equal(null, err);
+
+ User.findById(tj._id, function (err, user) {
+ db.close();
+ should.equal(null, err);
+
+ var expectedBuffer = new Buffer([123, 97, 97, 42, 11]);
+
+ should.equal(expectedBuffer.toString('base64'),
+ user.serial.toString('base64'), 'buffer mismatch');
+
+ tj.isModified('required').should.be.false;
+ tj.serial.copy(tj.required, 1);
+ tj.isModified('required').should.be.true;
+ should.equal('e3thYSo=', tj.required.toString('base64'));
+
+ // buffer method tests
+ var fns = {
+ 'writeUInt8': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt8(0x3, 0, 'big');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16(0xbeef, 0, 'little');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16LE(0xbeef, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt16BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt16BE(0xbeef, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32(0xfeedface, 0, 'little');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32LE(0xfeedface, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeUInt32BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeUInt32BE(0xfeedface, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt8': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt8(-5, 0, 'big');
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt16': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16(0x0023, 2, 'little');
+ tj.isModified('required').should.be.true;
+ tj.required[2].should.eql(0x23);
+ tj.required[3].should.eql(0x00);
+ }
+ , 'writeInt16LE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16LE(0x0023, 2);
+ tj.isModified('required').should.be.true;
+ tj.required[2].should.eql(0x23);
+ tj.required[3].should.eql(0x00);
+ }
+ , 'writeInt16BE': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt16BE(0x0023, 2);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt32': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32(0x23, 0, 'big');
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x00);
+ tj.required[2].should.eql(0x00);
+ tj.required[3].should.eql(0x23);
+ tj.required = new Buffer(8);
+ }
+ , 'writeInt32LE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32LE(0x23, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeInt32BE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeInt32BE(0x23, 0);
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x00);
+ tj.required[2].should.eql(0x00);
+ tj.required[3].should.eql(0x23);
+ }
+ , 'writeFloat': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloat(2.225073858507201e-308, 0, 'big');
+ tj.isModified('required').should.be.true;
+ tj.required[0].should.eql(0x00);
+ tj.required[1].should.eql(0x0f);
+ tj.required[2].should.eql(0xff);
+ tj.required[3].should.eql(0xff);
+ tj.required[4].should.eql(0xff);
+ tj.required[5].should.eql(0xff);
+ tj.required[6].should.eql(0xff);
+ tj.required[7].should.eql(0xff);
+ }
+ , 'writeFloatLE': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloatLE(2.225073858507201e-308, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeFloatBE': function () {
+ tj.required = new Buffer(16);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeFloatBE(2.225073858507201e-308, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeDoubleLE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeDoubleLE(0xdeadbeefcafebabe, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'writeDoubleBE': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.writeDoubleBE(0xdeadbeefcafebabe, 0);
+ tj.isModified('required').should.be.true;
+ }
+ , 'fill': function () {
+ tj.required = new Buffer(8);
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.fill(0);
+ tj.isModified('required').should.be.true;
+ for (var i = 0; i < tj.required.length; i++) {
+ tj.required[i].should.eql(0);
+ }
+ }
+ , 'set': function () {
+ reset(tj);
+ tj.isModified('required').should.be.false;
+ tj.required.set(0, 1);
+ tj.isModified('required').should.be.true;
+ }
+ };
+
+ var keys = Object.keys(fns)
+ , i = keys.length
+ , key
+
+ while (i--) {
+ key = keys[i];
+ if (Buffer.prototype[key]) {
+ fns[key]();
+ }
+ }
+ });
+ });
+ });
+ });
+
+ function reset (model) {
+ // internal
+ model._activePaths.clear('modify');
+ model.schema.requiredPaths.forEach(function (path) {
+ model._activePaths.require(path);
+ });
+ }
+ }
+};
diff --git a/node_modules/mongoose/test/types.document.test.js b/node_modules/mongoose/test/types.document.test.js
new file mode 100644
index 0000000..a422403
--- /dev/null
+++ b/node_modules/mongoose/test/types.document.test.js
@@ -0,0 +1,197 @@
+
+/**
+ * Module dependencies.
+ */
+
+var should = require('should')
+ , start = require('./common')
+ , mongoose = start.mongoose
+ , EmbeddedDocument = require('../lib/types/embedded')
+ , DocumentArray = require('../lib/types/documentarray')
+ , Schema = mongoose.Schema
+ , SchemaType = mongoose.SchemaType
+ , ValidatorError = SchemaType.ValidatorError
+ , ValidationError = mongoose.Document.ValidationError
+
+/**
+ * Setup.
+ */
+
+function Subdocument () {
+ EmbeddedDocument.call(this, {}, new DocumentArray);
+};
+
+/**
+ * Inherits from EmbeddedDocument.
+ */
+
+Subdocument.prototype.__proto__ = EmbeddedDocument.prototype;
+
+/**
+ * Set schema.
+ */
+
+Subdocument.prototype.schema = new Schema({
+ test: { type: String, required: true }
+ , work: { type: String, validate: /^good/ }
+});
+
+/**
+ * Schema.
+ */
+
+var RatingSchema = new Schema({
+ stars: Number
+});
+
+var MovieSchema = new Schema({
+ title: String
+ , ratings: [RatingSchema]
+});
+
+mongoose.model('Movie', MovieSchema);
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that save fires errors': function(){
+ var a = new Subdocument();
+ a.set('test', '');
+ a.set('work', 'nope');
+
+ a.save(function(err){
+ err.should.be.an.instanceof(ValidationError);
+ err.toString().should.eql('ValidationError: Validator "required" failed for path test, Validator failed for path work');
+ });
+ },
+
+ 'test that save fires with null if no errors': function(){
+ var a = new Subdocument();
+ a.set('test', 'cool');
+ a.set('work', 'goods');
+
+ a.save(function(err){
+ should.strictEqual(err, null);
+ });
+ },
+
+ 'objects can be passed to #set': function () {
+ var a = new Subdocument();
+ a.set({ test: 'paradiddle', work: 'good flam'});
+ a.test.should.eql('paradiddle');
+ a.work.should.eql('good flam');
+ },
+
+ 'Subdocuments can be passed to #set': function () {
+ var a = new Subdocument();
+ a.set({ test: 'paradiddle', work: 'good flam'});
+ a.test.should.eql('paradiddle');
+ a.work.should.eql('good flam');
+ var b = new Subdocument();
+ b.set(a);
+ b.test.should.eql('paradiddle');
+ b.work.should.eql('good flam');
+ },
+
+ 'cached _ids': function () {
+ var db = start();
+ var Movie = db.model('Movie');
+ db.close();
+ var m = new Movie;
+
+ should.equal(m.id, m.__id);
+ var old = m.id;
+ m._id = new mongoose.Types.ObjectId;
+ should.equal(m.id, m.__id);
+ should.strictEqual(true, old !== m.__id);
+
+ var m2= new Movie;
+ delete m2._doc._id;
+ m2.init({ _id: new mongoose.Types.ObjectId });
+ should.equal(m2.id, m2.__id);
+ should.strictEqual(true, m.__id !== m2.__id);
+ should.strictEqual(true, m.id !== m2.id);
+ should.strictEqual(true, m.__id !== m2.__id);
+ }
+
+ /*
+ 'Subdocument#remove': function (beforeExit) {
+ var db = start();
+ var Movie = db.model('Movie');
+
+ var super8 = new Movie({ title: 'Super 8' });
+
+ var id1 = '4e3d5fc7da5d7eb635063c96';
+ var id2 = '4e3d5fc7da5d7eb635063c97';
+ var id3 = '4e3d5fc7da5d7eb635063c98';
+ var id4 = '4e3d5fc7da5d7eb635063c99';
+
+ super8.ratings.push({ stars: 9, _id: id1 });
+ super8.ratings.push({ stars: 8, _id: id2 });
+ super8.ratings.push({ stars: 7, _id: id3 });
+ super8.ratings.push({ stars: 6, _id: id4 });
+ console.error('pre save', super8);
+
+ super8.save(function (err) {
+ should.strictEqual(err, null);
+
+ super8.title.should.eql('Super 8');
+ super8.ratings.id(id1).stars.valueOf().should.eql(9);
+ super8.ratings.id(id2).stars.valueOf().should.eql(8);
+ super8.ratings.id(id3).stars.valueOf().should.eql(7);
+ super8.ratings.id(id4).stars.valueOf().should.eql(6);
+
+ super8.ratings.id(id1).stars = 5;
+ super8.ratings.id(id2).remove();
+ super8.ratings.id(id3).stars = 4;
+ super8.ratings.id(id4).stars = 3;
+
+ super8.save(function (err) {
+ should.strictEqual(err, null);
+
+ Movie.findById(super8._id, function (err, movie) {
+ should.strictEqual(err, null);
+
+ movie.title.should.eql('Super 8');
+ movie.ratings.length.should.equal(3);
+ movie.ratings.id(id1).stars.valueOf().should.eql(5);
+ movie.ratings.id(id3).stars.valueOf().should.eql(4);
+ movie.ratings.id(id4).stars.valueOf().should.eql(3);
+
+ console.error('after save + findbyId', movie);
+
+ movie.ratings.id(id1).stars = 2;
+ movie.ratings.id(id3).remove();
+ movie.ratings.id(id4).stars = 1;
+
+ console.error('after modified', movie);
+
+ movie.save(function (err) {
+ should.strictEqual(err, null);
+
+ Movie.findById(super8._id, function (err, movie) {
+ db.close();
+ should.strictEqual(err, null);
+ movie.ratings.length.should.equal(2);
+ movie.ratings.id(id1).stars.valueOf().should.eql(2);
+ movie.ratings.id(id4).stars.valueOf().should.eql(1);
+ console.error('after resave + findById', movie);
+ });
+ });
+ });
+ });
+ });
+
+ beforeExit(function () {
+ var db = start();
+ Movie.remove({}, function (err) {
+ db.close();
+ });
+ });
+ }
+ */
+
+};
diff --git a/node_modules/mongoose/test/types.documentarray.test.js b/node_modules/mongoose/test/types.documentarray.test.js
new file mode 100644
index 0000000..ff8b381
--- /dev/null
+++ b/node_modules/mongoose/test/types.documentarray.test.js
@@ -0,0 +1,132 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , MongooseArray = mongoose.Types.Array
+ , MongooseDocumentArray = mongoose.Types.DocumentArray
+ , EmbeddedDocument = require('../lib/types/embedded')
+ , DocumentArray = require('../lib/types/documentarray')
+ , Schema = mongoose.Schema
+
+/**
+ * Setup.
+ */
+
+function TestDoc (schema) {
+ var Subdocument = function () {
+ EmbeddedDocument.call(this, {}, new DocumentArray);
+ };
+
+ /**
+ * Inherits from EmbeddedDocument.
+ */
+
+ Subdocument.prototype.__proto__ = EmbeddedDocument.prototype;
+
+ /**
+ * Set schema.
+ */
+
+ var SubSchema = new Schema({
+ title: { type: String }
+ });
+
+ Subdocument.prototype.schema = schema || SubSchema;
+
+ return Subdocument;
+}
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose array behaves and quacks like an array': function(){
+ var a = new MongooseDocumentArray();
+
+ a.should.be.an.instanceof(Array);
+ a.should.be.an.instanceof(MongooseArray);
+ a.should.be.an.instanceof(MongooseDocumentArray);
+ Array.isArray(a).should.be.true;
+ a._atomics.constructor.name.should.equal('Object');
+ 'object'.should.eql(typeof a);
+
+ var b = new MongooseArray([1,2,3,4]);
+ 'object'.should.eql(typeof b);
+ Object.keys(b.toObject()).length.should.equal(4);
+ },
+
+ '#id': function () {
+ var Subdocument = TestDoc();
+
+ var sub1 = new Subdocument();
+ sub1.title = 'Hello again to all my friends';
+ var id = sub1.id;
+
+ var a = new MongooseDocumentArray([sub1]);
+ a.id(id).title.should.equal('Hello again to all my friends');
+ a.id(sub1._id).title.should.equal('Hello again to all my friends');
+
+ // test with custom string _id
+ var Custom = new Schema({
+ title: { type: String }
+ , _id: { type: String, required: true }
+ });
+
+ var Subdocument = TestDoc(Custom);
+
+ var sub2 = new Subdocument();
+ sub2.title = 'together we can play some rock-n-roll';
+ sub2._id = 'a25';
+ var id2 = sub2.id;
+
+ var a = new MongooseDocumentArray([sub2]);
+ a.id(id2).title.should.equal('together we can play some rock-n-roll');
+ a.id(sub2._id).title.should.equal('together we can play some rock-n-roll');
+
+ // test with custom number _id
+ var CustNumber = new Schema({
+ title: { type: String }
+ , _id: { type: Number, required: true }
+ });
+
+ var Subdocument = TestDoc(CustNumber);
+
+ var sub3 = new Subdocument();
+ sub3.title = 'rock-n-roll';
+ sub3._id = 1995;
+ var id3 = sub3.id;
+
+ var a = new MongooseDocumentArray([sub3]);
+ a.id(id3).title.should.equal('rock-n-roll');
+ a.id(sub3._id).title.should.equal('rock-n-roll');
+ },
+
+ 'inspect works with bad data': function () {
+ var threw = false;
+ var a = new MongooseDocumentArray([null]);
+ try {
+ a.inspect();
+ } catch (err) {
+ threw = true;
+ console.error(err.stack);
+ }
+ threw.should.be.false;
+ },
+
+ 'toObject works with bad data': function () {
+ var threw = false;
+ var a = new MongooseDocumentArray([null]);
+ try {
+ a.toObject();
+ } catch (err) {
+ threw = true;
+ console.error(err.stack);
+ }
+ threw.should.be.false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/types.number.test.js b/node_modules/mongoose/test/types.number.test.js
new file mode 100644
index 0000000..68ebe5e
--- /dev/null
+++ b/node_modules/mongoose/test/types.number.test.js
@@ -0,0 +1,37 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('./common').mongoose
+ , MongooseNumber = mongoose.Types.Number
+ , SchemaNumber = mongoose.Schema.Types.Number
+ , should = require('should')
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'test that a mongoose number behaves and quacks like a number': function(){
+ var a = new MongooseNumber(5);
+
+ a.should.be.an.instanceof(Number);
+ a.should.be.an.instanceof(MongooseNumber);
+ a.toString().should.eql('5');
+
+ (a._atomics.constructor).should.eql(Object);
+ },
+
+ 'an empty string casts to null': function () {
+ var n = new SchemaNumber();
+ should.strictEqual(n.cast(''), null);
+ },
+
+ 'a null number should castForQuery to null': function () {
+ var n = new SchemaNumber();
+ should.strictEqual(n.castForQuery(null), null);
+ }
+
+};
diff --git a/node_modules/mongoose/test/utils.test.js b/node_modules/mongoose/test/utils.test.js
new file mode 100644
index 0000000..b275578
--- /dev/null
+++ b/node_modules/mongoose/test/utils.test.js
@@ -0,0 +1,196 @@
+
+/**
+ * Module dependencies.
+ */
+
+var start = require('./common')
+ , mongoose = start.mongoose
+ , Schema = mongoose.Schema
+ , utils = require('../lib/utils')
+ , StateMachine = require('../lib/statemachine')
+ , ObjectId = require('../lib/types/objectid')
+ , MongooseBuffer = require('../lib/types/buffer')
+
+/**
+ * Setup.
+ */
+
+var ActiveRoster = StateMachine.ctor('require', 'init', 'modify');
+
+/**
+ * Test.
+ */
+
+module.exports = {
+
+ 'should detect a path as required if it has been required': function () {
+ var ar = new ActiveRoster();
+ ar.require('hello');
+ ar.stateOf('hello').should.equal('require');
+ },
+
+ 'should detect a path as inited if it has been inited': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.stateOf('hello').should.equal('init');
+ },
+
+ 'should detect a path as modified': function () {
+ var ar = new ActiveRoster();
+ ar.modify('hello');
+ ar.stateOf('hello').should.equal('modify');
+ },
+
+ 'should remove a path from an old state upon a state change': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('hello');
+ ar.states.init.should.not.have.property('hello');
+ ar.states.modify.should.have.property('hello');
+ },
+
+ 'forEach should be able to iterate through the paths belonging to one state': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach('init', function (path) {
+ ['hello', 'goodbye'].should.contain(path);
+ });
+ },
+
+ 'forEach should be able to iterate through the paths in the union of two or more states': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach('modify', 'require', function (path) {
+ ['world', 'foo'].should.contain(path);
+ });
+ },
+
+ 'forEach should iterate through all paths that have any state if given no state arguments': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.init('goodbye');
+ ar.modify('world');
+ ar.require('foo');
+ ar.forEach(function (path) {
+ ['hello', 'goodbye', 'world', 'foo'].should.contain(path);
+ });
+ },
+
+ 'should be able to detect if at least one path exists in a set of states': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.some('init').should.be.true;
+ ar.some('modify').should.be.true;
+ ar.some('require').should.be.false;
+ ar.some('init', 'modify').should.be.true;
+ ar.some('init', 'require').should.be.true;
+ ar.some('modify', 'require').should.be.true;
+ },
+
+ 'should be able to `map` over the set of paths in a given state': function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.require('iAmTheWalrus');
+ var suffixedPaths = ar.map('init', 'modify', function (path) {
+ return path + '-suffix';
+ });
+ suffixedPaths.should.eql(['hello-suffix', 'world-suffix']);
+ },
+
+ "should `map` over all states' paths if no states are specified in a `map` invocation": function () {
+ var ar = new ActiveRoster();
+ ar.init('hello');
+ ar.modify('world');
+ ar.require('iAmTheWalrus');
+ var suffixedPaths = ar.map(function (path) {
+ return path + '-suffix';
+ });
+ suffixedPaths.should.eql(['iAmTheWalrus-suffix', 'hello-suffix', 'world-suffix']);
+ },
+
+ 'test utils.options': function () {
+ var o = { a: 1, b: 2, c: 3, 0: 'zero1' };
+ var defaults = { b: 10, d: 20, 0: 'zero2' };
+ var result = utils.options(defaults, o);
+ result.a.should.equal(1);
+ result.b.should.equal(2);
+ result.c.should.equal(3);
+ result.d.should.equal(20);
+ o.d.should.equal(result.d);
+ result['0'].should.equal('zero1');
+
+ var result2 = utils.options(defaults);
+ result2.b.should.equal(10);
+ result2.d.should.equal(20);
+ result2['0'].should.equal('zero2');
+
+ // same properties/vals
+ defaults.should.eql(result2);
+
+ // same object
+ defaults.should.not.equal(result2);
+ },
+
+ 'test deepEquals on ObjectIds': function () {
+ var s = (new ObjectId).toString();
+
+ var a = new ObjectId(s)
+ , b = new ObjectId(s);
+
+ utils.deepEqual(a, b).should.be.true;
+ utils.deepEqual(a, a).should.be.true;
+ utils.deepEqual(a, new ObjectId).should.be.false;
+ },
+
+ 'deepEquals on MongooseDocumentArray works': function () {
+ var db = start()
+ , A = new Schema({ a: String })
+ , M = db.model('deepEqualsOnMongooseDocArray', new Schema({
+ a1: [A]
+ , a2: [A]
+ }));
+
+ db.close();
+
+ var m1 = new M({
+ a1: [{a: 'Hi'}, {a: 'Bye'}]
+ });
+
+ m1.a2 = m1.a1;
+ utils.deepEqual(m1.a1, m1.a2).should.be.true;
+
+ var m2 = new M;
+ m2.init(m1.toObject());
+
+ utils.deepEqual(m1.a1, m2.a1).should.be.true;
+
+ m2.set(m1.toObject());
+ utils.deepEqual(m1.a1, m2.a1).should.be.true;
+ },
+
+ // gh-688
+ 'deepEquals with MongooseBuffer': function () {
+ var str = "this is the day";
+ var a = new MongooseBuffer(str);
+ var b = new MongooseBuffer(str);
+ var c = new Buffer(str);
+ var d = new Buffer("this is the way");
+ var e = new Buffer("other length");
+
+ utils.deepEqual(a, b).should.be.true;
+ utils.deepEqual(a, c).should.be.true;
+ utils.deepEqual(a, d).should.be.false;
+ utils.deepEqual(a, e).should.be.false;
+ utils.deepEqual(a, []).should.be.false;
+ utils.deepEqual([], a).should.be.false;
+ }
+
+};
diff --git a/node_modules/mongoose/test/zzlast.test.js b/node_modules/mongoose/test/zzlast.test.js
new file mode 100644
index 0000000..706b7f1
--- /dev/null
+++ b/node_modules/mongoose/test/zzlast.test.js
@@ -0,0 +1,11 @@
+
+var should = require('should');
+var gleak = require('gleak')();
+gleak.whitelist.push('currentObjectStored', 'reg_exp'); // node-mongodb-native
+
+module.exports.globals = function (beforeExit) {
+ beforeExit(function () {
+ var leaks = gleak.detect();
+ should.strictEqual(leaks.length, 0, 'detected global var leaks: ' + leaks.join(', '));
+ })
+}