'use strict';
* Module dependencies.
*/
const start = require('./common');
const STATES = require('../lib/connectionState');
const assert = require('assert');
const mongodb = require('mongodb');
const MongooseError = require('../lib/error/index');
const CastError = require('../lib/error/cast');
const mongoose = start.mongoose;
const Schema = mongoose.Schema;
* Test.
*/
describe('connections:', function() {
this.timeout(10000);
describe('openUri (gh-5304)', function() {
it('with mongoose.createConnection()', function() {
const uriWithoutDb = start.uri.slice(0, start.uri.lastIndexOf('/'));
const dbAndQuery = start.uri.slice(start.uri.lastIndexOf('/') + 1);
const queryIndex = dbAndQuery.indexOf('?');
const query = queryIndex !== -1 ? dbAndQuery.slice(queryIndex) : '';
const newUri = uriWithoutDb + '/' + start.databases[0] + query;
const conn = mongoose.createConnection(newUri);
assert.equal(conn.constructor.name, 'NativeConnection');
const Test = conn.model('Test', new Schema({ name: String }));
assert.equal(Test.modelName, 'Test');
const findPromise = Test.findOne();
return conn.asPromise().
then(function(conn) {
assert.equal(conn.constructor.name, 'NativeConnection');
const match = /mongodb:\/\/([\d.]+)(?::(\d+))?(?:,[\d.]+(?::\d+)?)*\/(\w+)/i.exec(start.uri);
assert.ok(match);
assert.equal(conn.host, match[1]);
assert.equal(conn.port, parseInt(match[2]));
assert.equal(conn.name, start.databases[0]);
return findPromise;
}).
then(function() {
return conn.close();
});
});
it('with autoIndex (gh-5423)', async function() {
const conn = await mongoose.createConnection(start.uri, {
autoIndex: false
}).asPromise();
assert.strictEqual(conn.config.autoIndex, false);
await conn.close();
});
it('with autoCreate (gh-6489)', async function() {
const conn = await mongoose.createConnection(start.uri, {
}).asPromise();
const Model = conn.model('gh6489_Conn', new Schema({ name: String }, {
collation: { locale: 'en_US', strength: 1 },
collection: 'gh6489_Conn'
}));
await Model.init();
const collections = await conn.db.listCollections().toArray();
assert.ok(collections.map(c => c.name).includes('gh6489_Conn'));
await Model.create([{ name: 'alpha' }, { name: 'Zeta' }]);
const res = await conn.collection('gh6489_Conn').
find({}).sort({ name: 1 }).toArray();
assert.deepEqual(res.map(v => v.name), ['alpha', 'Zeta']);
await conn.close();
});
it('with autoCreate = false (gh-8814)', async function() {
const conn = await mongoose.createConnection(start.uri, {
autoCreate: false
}).asPromise();
const Model = conn.model('gh8814_Conn', new Schema({ name: String }, {
collation: { locale: 'en_US', strength: 1 },
collection: 'gh8814_Conn'
}));
await Model.init();
const res = await conn.listCollections();
assert.ok(!res.map(c => c.name).includes('gh8814_Conn'));
await conn.close();
});
it('autoCreate when collection already exists does not fail (gh-7122)', async function() {
const conn = await mongoose.createConnection(start.uri).asPromise();
const schema = new mongoose.Schema({
name: {
type: String,
index: { unique: true }
}
}, { autoCreate: true });
await conn.model('Actor', schema).init();
await conn.close();
});
it('throws helpful error with undefined uri (gh-6763)', async function() {
await assert.rejects(async function() {
await mongoose.createConnection(void 0).asPromise();
}, /string.*createConnection/);
});
it('connection plugins (gh-7378)', async function() {
const conn1 = mongoose.createConnection(start.uri);
const conn2 = mongoose.createConnection(start.uri);
const called = [];
conn1.plugin(schema => called.push(schema));
conn2.model('Test', new Schema({}));
assert.equal(called.length, 0);
const schema = new Schema({});
conn1.model('Test', schema);
assert.equal(called.length, 1);
assert.equal(called[0], schema);
await conn1.close();
await conn2.close();
});
});
describe('helpers', function() {
let conn;
before(async function() {
conn = mongoose.createConnection(start.uri2);
await conn.asPromise();
return conn;
});
after(function() {
return conn.close();
});
it('dropDatabase()', async function() {
await conn.dropDatabase();
});
it('dropCollection()', async function() {
await conn.db.collection('test').insertOne({ x: 1 });
await conn.dropCollection('test');
const doc = await conn.db.collection('test').findOne();
assert.ok(!doc);
});
it('createCollection()', async function() {
await conn.dropDatabase();
await conn.createCollection('gh5712', {
capped: true,
size: 1024
});
const collections = await conn.listCollections();
const names = collections.map(function(c) { return c.name; });
assert.ok(names.indexOf('gh5712') !== -1);
assert.ok(collections[names.indexOf('gh5712')].options.capped);
await conn.createCollection('gh5712_0');
const collectionsAfterCreation = await conn.listCollections();
const newCollectionsNames = collectionsAfterCreation.map(function(c) { return c.name; });
assert.ok(newCollectionsNames.indexOf('gh5712') !== -1);
});
it('listCollections()', async function() {
await conn.dropDatabase();
await conn.createCollection('test1176');
await conn.createCollection('test94112');
const collections = await conn.listCollections();
assert.deepStrictEqual(collections.map(coll => coll.name).sort(), ['test1176', 'test94112']);
});
});
describe('events', function() {
let conn;
before(async function() {
conn = mongoose.createConnection(start.uri2, { monitorCommands: true });
await conn.asPromise();
await conn.collection('test').deleteMany({});
return conn;
});
after(function() {
return conn.close();
});
it('operation-start', async function() {
const events = [];
conn.on('operation-start', ev => events.push(ev));
await conn.collection('test').findOne({ answer: 42 });
assert.equal(events.length, 1);
assert.equal(events[0].collectionName, 'test');
assert.equal(events[0].method, 'findOne');
assert.deepStrictEqual(events[0].params, [{ answer: 42 }]);
await conn.collection('test').insertOne({ _id: 12, answer: 99 });
assert.equal(events.length, 2);
assert.equal(events[1].collectionName, 'test');
assert.equal(events[1].method, 'insertOne');
assert.deepStrictEqual(events[1].params, [{ _id: 12, answer: 99 }]);
});
it('operation-end', async function() {
const events = [];
conn.on('operation-end', ev => {
events.push(ev);
});
await conn.collection('test').insertOne({ _id: 17, answer: 42 });
assert.equal(events.length, 1);
assert.equal(events[0].collectionName, 'test');
assert.equal(events[0].method, 'insertOne');
await conn.collection('test').findOne({ answer: 42 });
assert.equal(events.length, 2);
assert.equal(events[1].collectionName, 'test');
assert.equal(events[1].method, 'findOne');
assert.deepStrictEqual(events[1].result, { _id: 17, answer: 42 });
});
it('commandStarted, commandFailed, commandSucceeded (gh-14611)', async function() {
let events = [];
conn.on('commandStarted', event => events.push(event));
conn.on('commandFailed', event => events.push(event));
conn.on('commandSucceeded', event => events.push(event));
await conn.collection('test').insertOne({ _id: 14611, answer: 42 });
assert.equal(events.length, 2);
assert.equal(events[0].constructor.name, 'CommandStartedEvent');
assert.equal(events[0].commandName, 'insert');
assert.equal(events[1].constructor.name, 'CommandSucceededEvent');
assert.equal(events[1].requestId, events[0].requestId);
events = [];
await conn.createCollection('tests', { capped: 1024 }).catch(() => {});
assert.equal(events.length, 2);
assert.equal(events[0].constructor.name, 'CommandStartedEvent');
assert.equal(events[0].commandName, 'create');
assert.equal(events[1].constructor.name, 'CommandFailedEvent');
assert.equal(events[1].requestId, events[0].requestId);
});
});
it('should allow closing a closed connection', async function() {
const db = mongoose.createConnection();
assert.equal(db.readyState, 0);
await db.close();
});
describe('errors', function() {
it('.catch() means error does not get thrown (gh-5229)', function(done) {
const db = mongoose.createConnection();
db.openUri('fail connection').catch(function(error) {
assert.ok(error);
done();
});
});
it('promise is rejected even if there is an error event listener (gh-7850)', function(done) {
const db = mongoose.createConnection();
let called = 0;
db.on('error', () => ++called);
db.openUri('fail connection').catch(function(error) {
assert.ok(error);
setTimeout(() => {
assert.equal(called, 1);
done();
}, 0);
});
});
it('readyState is disconnected if initial connection fails (gh-6244)', async function() {
const db = mongoose.createConnection();
let threw = false;
try {
await db.openUri('fail connection');
} catch (err) {
assert.ok(err);
assert.equal(err.name, 'MongoParseError');
threw = true;
}
assert.ok(threw);
assert.strictEqual(db.readyState, 0);
});
});
it('should return an error if malformed uri passed', async function() {
const err = await mongoose.createConnection('mongodb:///fake').asPromise().then(() => null, err => err);
assert.ok(err);
assert.equal(err.name, 'MongoParseError');
});
describe('.model()', function() {
let db;
before(function() {
db = start();
});
after(async function() {
await db.close();
});
beforeEach(function() {
db.deleteModel(/.*/);
});
it('allows passing a schema', function() {
mongoose.deleteModel(/Test/);
const MyModel = mongoose.model('Test', new Schema({
name: String
}));
assert.ok(MyModel.schema instanceof Schema);
assert.ok(MyModel.prototype.schema instanceof Schema);
const m = new MyModel({ name: 'aaron' });
assert.equal(m.name, 'aaron');
});
it('should properly assign the db', function() {
const A = mongoose.model('testing853a', new Schema({ x: String }), 'testing853-1');
const B = mongoose.model('testing853b', new Schema({ x: String }), 'testing853-2');
const C = B.model('testing853a');
assert.ok(C === A);
});
it('prevents overwriting pre-existing models', function() {
db.deleteModel(/Test/);
db.model('Test', new Schema());
assert.throws(function() {
db.model('Test', new Schema());
}, /Cannot overwrite `Test` model/);
});
it('allows passing identical name + schema args', function() {
const name = 'Test';
const schema = new Schema();
db.deleteModel(/Test/);
const model = db.model(name, schema);
db.model(name, model.schema);
});
it('throws on unknown model name', function() {
assert.throws(function() {
db.model('iDoNotExist!');
}, /Schema hasn't been registered/);
});
describe('passing collection name', function() {
describe('when model name already exists', function() {
it('returns a new uncached model', function() {
const s1 = new Schema({ a: [] });
const name = 'Test';
const A = db.model(name, s1);
const B = db.model(name);
const C = db.model(name, 'alternate');
assert.ok(A.collection.name === B.collection.name);
assert.ok(A.collection.name !== C.collection.name);
assert.ok(db.models[name].collection.name !== C.collection.name);
assert.ok(db.models[name].collection.name === A.collection.name);
});
});
});
describe('passing object literal schemas', function() {
it('works', async function() {
const A = db.model('A', { n: [{ age: 'number' }] });
const a = new A({ n: [{ age: '47' }] });
assert.strictEqual(47, a.n[0].age);
await a.save();
await A.findById(a);
assert.strictEqual(47, a.n[0].age);
});
});
});
it('force close (gh-5664)', function(done) {
const opts = {};
const db = mongoose.createConnection(start.uri, opts);
const coll = db.collection('Test');
db.asPromise().then(function() {
setTimeout(function() {
coll.insertOne({ x: 1 }, function(error) {
assert.ok(error);
done();
});
}, 100);
db.close(true);
});
});
it('destroy connection and remove it permanently', async function() {
const opts = {};
const conn = await mongoose.createConnection(start.uri, opts).asPromise();
conn.useDb('test-db');
const totalConn = mongoose.connections.length;
await conn.destroy();
assert.equal(mongoose.connections.length, totalConn - 1);
});
it('verify that attempt to re-open destroyed connection throws error, via promise', async function() {
const opts = {};
const conn = await mongoose.createConnection(start.uri, opts).asPromise();
conn.useDb('test-db');
await conn.destroy();
try {
await conn.openUri(start.uri);
} catch (error) {
assert.equal(error.message, 'Connection has been closed and destroyed, and cannot be used for re-opening the connection. Please create a new connection with `mongoose.createConnection()` or `mongoose.connect()`.');
}
});
it('can re-open after close with useDb() (gh-15531)', async function() {
const opts = {};
const conn = await mongoose.createConnection(start.uri, opts).asPromise();
conn.useDb('test-db');
await conn.close();
await conn.openUri(start.uri);
assert.strictEqual(conn.readyState, 1);
await conn.collection('Test').insertOne({ x: 1 });
});
it('verify that attempt to re-open destroyed connection throws error, via callback', async function() {
const opts = {};
const conn = await mongoose.createConnection(start.uri, opts).asPromise();
conn.useDb('test-db');
await conn.destroy();
await assert.rejects(
() => conn.openUri(start.uri),
/Connection has been closed and destroyed/
);
});
it('force close with connection created after close (gh-5664)', function(done) {
const opts = {};
const db = mongoose.createConnection(start.uri, opts);
db.asPromise().then(function() {
setTimeout(function() {
let threw = false;
try {
db.collection('Test').insertOne({ x: 1 });
} catch (error) {
threw = true;
assert.ok(error);
}
assert.ok(threw);
done();
}, 100);
db.close(true);
});
});
it('bufferCommands (gh-5720)', function() {
let opts = { bufferCommands: false };
let db = mongoose.createConnection(start.uri, opts);
let M = db.model('gh5720', new Schema({}));
assert.ok(!M.collection._shouldBufferCommands());
db.close();
opts = { bufferCommands: true };
db = mongoose.createConnection(start.uri, opts);
M = db.model('gh5720', new Schema({}, { bufferCommands: false }));
assert.ok(!M.collection._shouldBufferCommands());
db.close();
opts = { bufferCommands: true };
db = mongoose.createConnection(start.uri, opts);
M = db.model('gh5720', new Schema({}));
assert.ok(M.collection._shouldBufferCommands());
db = mongoose.createConnection();
M = db.model('gh5720', new Schema({}));
opts = { bufferCommands: false };
db.openUri(start.uri, opts);
assert.ok(!M.collection._shouldBufferCommands());
return M.findOne().
then(
() => assert.ok(false),
err => assert.ok(err.message.includes('initial connection'))
).
then(() => db.close());
});
it('dbName option (gh-6106)', function() {
const opts = { dbName: 'bacon' };
return mongoose.
createConnection(start.uri, opts).
asPromise().
then(db => {
assert.equal(db.name, 'bacon');
db.close();
});
});
it('uses default database in uri if options.dbName is not provided', async function() {
const uriWithoutDb = start.uri.slice(0, start.uri.lastIndexOf('/'));
const dbAndQuery = start.uri.slice(start.uri.lastIndexOf('/') + 1);
const queryIndex = dbAndQuery.indexOf('?');
const query = queryIndex !== -1 ? dbAndQuery.slice(queryIndex) : '';
const newUri = uriWithoutDb + '/default-db-name' + query;
const db = await mongoose.createConnection(newUri).asPromise();
assert.equal(db.name, 'default-db-name');
await db.close();
});
it('startSession() (gh-6653)', function() {
const conn = mongoose.createConnection(start.uri);
let lastUse;
let session;
return conn.startSession().
then(_session => {
session = _session;
assert.ok(session);
lastUse = session.serverSession.lastUse;
return new Promise(resolve => setTimeout(resolve, 10));
}).then(() => {
return conn.model('Test', new Schema({})).findOne({}, null, { session });
}).
then(() => {
assert.ok(session.serverSession.lastUse > lastUse);
return conn.close();
});
});
describe('modelNames()', function() {
it('returns names of all models registered on it', async function() {
const m = new mongoose.Mongoose();
m.model('root', { x: String });
const another = m.model('another', { x: String });
another.discriminator('discriminated', new Schema({ x: String }));
const db = m.createConnection();
db.model('something', { x: String });
let names = db.modelNames();
assert.ok(Array.isArray(names));
assert.equal(names.length, 1);
assert.equal(names[0], 'something');
names = m.modelNames();
assert.ok(Array.isArray(names));
assert.equal(names.length, 3);
assert.equal(names[0], 'root');
assert.equal(names[1], 'another');
assert.equal(names[2], 'discriminated');
await db.close();
});
});
describe('connection pool sharing: ', function() {
it('works', async function() {
const db = mongoose.createConnection(start.uri);
const db2 = db.useDb('mongoose2');
assert.equal('mongoose2', db2.name);
assert.equal(db2.port, db.port);
assert.equal(db2.replica, db.replica);
assert.equal(db2.hosts, db.hosts);
assert.equal(db2.host, db.host);
assert.equal(db2.port, db.port);
assert.equal(db2.user, db.user);
assert.equal(db2.pass, db.pass);
assert.deepEqual(db.options, db2.options);
await db2.close();
});
it('saves correctly', async function() {
const db = start();
const db2 = db.useDb(start.databases[1]);
const schema = new Schema({
body: String,
thing: Number
});
const m1 = db.model('Test', schema);
const m2 = db2.model('Test', schema);
const i1 = await m1.create({ body: 'this is some text', thing: 1 });
const i2 = await m2.create({ body: 'this is another body', thing: 2 });
const item1 = await m1.findById(i1.id);
assert.equal('this is some text', item1.body);
assert.equal(1, item1.thing);
const item2 = await m2.findById(i2.id);
assert.equal('this is another body', item2.body);
assert.equal(2, item2.thing);
const nothing = await m1.findById(i2.id);
assert.strictEqual(null, nothing);
const nothing2 = await m2.findById(i1.id);
assert.strictEqual(null, nothing2);
await db.close();
await db2.close();
});
it('emits connecting events on both', async function() {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('connecting', async function() {
hit && await close();
hit = true;
});
db.on('connecting', async function() {
hit && await close();
hit = true;
});
db.openUri(start.uri);
async function close() {
await db.close();
}
});
it('emits connected events on both', function() {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('connected', function() {
hit && close();
hit = true;
});
db.on('connected', function() {
hit && close();
hit = true;
});
db.openUri(start.uri);
async function close() {
await db.close();
}
});
it('emits open events on both', function() {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('open', function() {
hit && close();
hit = true;
});
db.on('open', function() {
hit && close();
hit = true;
});
db.openUri(start.uri);
async function close() {
await db.close();
}
});
it('emits disconnecting events on both, closing initial db', function(done) {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('disconnecting', function() {
hit && done();
hit = true;
});
db.on('disconnecting', function() {
hit && done();
hit = true;
});
db.on('open', function() {
db.close();
});
db.openUri(start.uri);
});
it('emits disconnecting events on both, closing secondary db', function(done) {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('disconnecting', function() {
hit && done();
hit = true;
});
db.on('disconnecting', function() {
hit && done();
hit = true;
});
db.on('open', function() {
db2.close();
});
db.openUri(start.uri);
});
it('emits disconnected events on both, closing initial db', function(done) {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('disconnected', function() {
hit && done();
hit = true;
});
db.on('disconnected', function() {
hit && done();
hit = true;
});
db.on('open', function() {
db.close();
});
db.openUri(start.uri);
});
it('emits disconnected events on both, closing secondary db', function(done) {
const db = mongoose.createConnection();
const db2 = db.useDb(start.databases[1]);
let hit = false;
db2.on('disconnected', function() {
hit && done();
hit = true;
});
db.on('disconnected', function() {
hit && done();
hit = true;
});
db.on('open', function() {
db2.close();
});
db.openUri(start.uri);
});
it('closes correctly for all dbs, closing initial db', async function() {
const db = await start({ noErrorListener: true }).asPromise();
const db2 = db.useDb(start.databases[1]);
const p = new Promise(resolve => {
db2.on('close', function() {
resolve();
});
});
await db.close();
await p;
});
it('handles re-opening base connection (gh-11240)', async function() {
const db = await start().asPromise();
const db2 = db.useDb(start.databases[1]);
await db.close();
await db.openUri(start.uri);
assert.strictEqual(db.client, db2.client);
await db.close();
});
it('closes correctly for all dbs, closing secondary db', function(done) {
const db = start();
const db2 = db.useDb(start.databases[1]);
db.on('disconnected', function() {
done();
});
db2.close();
});
it('cache connections to the same db', function() {
const db = start();
const db2 = db.useDb(start.databases[1], { useCache: true });
const db3 = db.useDb(start.databases[1], { useCache: true });
assert.strictEqual(db2, db3);
return db.close();
});
it('supports removing db (gh-11821)', async function() {
const db = await mongoose.createConnection(start.uri).asPromise();
const schema = mongoose.Schema({ name: String }, { autoCreate: false, autoIndex: false });
const Test = db.model('Test', schema);
await Test.deleteMany({});
await Test.create({ name: 'gh-11821' });
const db2 = db.useDb(start.databases[1]);
const Test2 = db2.model('Test', schema);
await Test2.deleteMany({});
let doc = await Test2.findOne();
assert.equal(doc, null);
db.removeDb(start.databases[1]);
assert.equal(db2.readyState, STATES.disconnected);
assert.equal(db.readyState, STATES.connected);
await assert.rejects(
() => Test2.findOne(),
/Connection was force closed/
);
doc = await Test.findOne();
assert.equal(doc.name, 'gh-11821');
await db.close();
});
it('updates child dbs lastHeartbeatAt (gh-15635)', async function() {
const db = await mongoose.createConnection(start.uri).asPromise();
const schema = mongoose.Schema({ name: String }, { autoCreate: false, autoIndex: false });
const Test = db.model('Test', schema);
await Test.deleteMany({});
await Test.create({ name: 'gh-11821' });
const db2 = db.useDb(start.databases[1]);
const now = Date.now();
db.client.emit('serverHeartbeatSucceeded');
assert.ok(db._lastHeartbeatAt >= now);
assert.ok(db2._lastHeartbeatAt >= now);
});
it('heartbeat handler uses snapshotted Date, not affected by faked globalThis.Date (gh-16183)', async function() {
const db = await mongoose.createConnection(start.uri).asPromise();
const OriginalDate = globalThis.Date;
try {
const FAKE_NOW = 1;
globalThis.Date = { now: () => FAKE_NOW };
const realBefore = OriginalDate.now();
db.client.emit('serverHeartbeatSucceeded');
const realAfter = OriginalDate.now();
assert.notStrictEqual(db._lastHeartbeatAt, FAKE_NOW);
assert.ok(db._lastHeartbeatAt >= realBefore);
assert.ok(db._lastHeartbeatAt <= realAfter);
} finally {
globalThis.Date = OriginalDate;
await db.close();
}
});
it('flushes buffered operations when heartbeat refreshes stale connection (gh-16183)', async function() {
const db = await mongoose.createConnection(start.uri).asPromise();
db._lastHeartbeatAt = 1;
assert.equal(db._readyState, STATES.connected);
assert.equal(db.readyState, STATES.disconnected);
let flushed = false;
db._queue.push({ fn: () => { flushed = true; } });
db.client.emit('serverHeartbeatSucceeded');
assert.equal(db.readyState, STATES.connected);
assert.strictEqual(flushed, true);
assert.equal(db._queue.length, 0);
await db.close();
});
it('flushes buffered operations on child dbs when heartbeat refreshes stale connection (gh-16183)', async function() {
const db = await mongoose.createConnection(start.uri).asPromise();
const db2 = db.useDb(start.databases[1]);
db._lastHeartbeatAt = 1;
db2._lastHeartbeatAt = 1;
let childFlushed = false;
db2._queue.push({ fn: () => { childFlushed = true; } });
db.client.emit('serverHeartbeatSucceeded');
assert.strictEqual(childFlushed, true);
assert.equal(db2._queue.length, 0);
await db.close();
});
});
describe('shouldAuthenticate()', function() {
describe('when using standard authentication', function() {
describe('when username and password are undefined', function() {
it('should return false', function() {
const db = mongoose.createConnection(start.uri, {});
assert.equal(db.shouldAuthenticate(), false);
return db.close();
});
});
describe('when username and password are empty strings', function() {
it('should return false', function() {
const db = mongoose.createConnection(start.uri, {
user: '',
pass: ''
});
db.on('error', function() {});
assert.equal(db.shouldAuthenticate(), false);
return db.close();
});
});
describe('when both username and password are defined', function() {
it('should return true', function() {
const db = mongoose.createConnection(start.uri, {
user: 'user',
pass: 'pass'
});
db.asPromise().catch(() => {});
assert.equal(db.shouldAuthenticate(), true);
return db.close();
});
});
});
describe('when using MONGODB-X509 authentication', function() {
describe('when username and password are undefined', function() {
it('should return false', function() {
const db = mongoose.createConnection(start.uri, {});
db.on('error', function() {
});
assert.equal(db.shouldAuthenticate(), false);
return db.close();
});
});
describe('when only username is defined', function() {
it('should return false', function() {
const db = mongoose.createConnection(start.uri, {
user: 'user',
auth: { authMechanism: 'MONGODB-X509' }
});
db.asPromise().catch(() => {});
assert.equal(db.shouldAuthenticate(), true);
return db.close();
});
});
describe('when both username and password are defined', function() {
it('should return false', function() {
const db = mongoose.createConnection(start.uri, {
user: 'user',
pass: 'pass',
auth: { authMechanism: 'MONGODB-X509' }
});
db.asPromise().catch(() => {});
assert.equal(db.shouldAuthenticate(), true);
return db.close();
});
});
});
});
describe('passing a function into createConnection', function() {
it('should store the name of the function (gh-6517)', async function() {
const conn = mongoose.createConnection(start.uri);
const schema = new Schema({ name: String });
class Person extends mongoose.Model {}
const PersonModel = conn.model(Person, schema);
assert.strictEqual(conn.modelNames()[0], 'Person');
await conn.asPromise();
await PersonModel.init();
await conn.close();
});
});
it('deleteModel()', async function() {
const conn = mongoose.createConnection(start.uri);
let Model = conn.model('gh6813', new Schema({ name: String }));
const events = [];
conn.on('deleteModel', model => events.push(model));
assert.ok(conn.model('gh6813'));
conn.deleteModel('gh6813');
assert.equal(events.length, 1);
assert.equal(events[0], Model);
assert.throws(function() {
conn.model('gh6813');
}, /Schema hasn't been registered/);
Model = conn.model('gh6813', new Schema({ name: String }));
assert.ok(Model);
await Model.create({ name: 'test' });
await conn.close();
});
it('throws a MongooseServerSelectionError on server selection timeout (gh-8451)', async function() {
const opts = {
serverSelectionTimeoutMS: 100
};
const uri = 'mongodb://baddomain:27017/test';
const err = await mongoose.createConnection(uri, opts).
asPromise().
then(() => null, err => err);
assert.ok(err);
assert.equal(err.name, 'MongooseServerSelectionError');
});
it('avoids unhandled error on createConnection() if error handler registered (gh-14377)', async function() {
const opts = {
serverSelectionTimeoutMS: 100
};
const uri = 'mongodb://baddomain:27017/test';
const conn = mongoose.createConnection(uri, opts);
await new Promise(resolve => {
conn.on('error', err => {
assert.equal(err.name, 'MongoServerSelectionError');
resolve();
});
});
});
it('`watch()` on a whole collection (gh-8425)', async function() {
this.timeout(10000);
if (!process.env.REPLICA_SET) {
this.skip();
}
const opts = {
replicaSet: process.env.REPLICA_SET
};
const conn = await mongoose.createConnection(start.uri, opts);
const Model = conn.model('Test', Schema({ name: String }));
await Model.create({ name: 'test' });
const changeStream = conn.watch();
const changes = [];
changeStream.on('change', data => {
changes.push(data);
});
await new Promise((resolve) => changeStream.on('ready', () => resolve()));
const nextChange = new Promise(resolve => changeStream.on('change', resolve));
await Model.create({ name: 'test2' });
await nextChange;
assert.equal(changes.length, 1);
assert.equal(changes[0].operationType, 'insert');
await changeStream.close();
await conn.close();
});
it('useDB inherits config from default connection (gh-8267)', async function() {
const m = new mongoose.Mongoose();
await m.connect(start.uri, { sanitizeFilter: true });
const db2 = m.connection.useDb('gh8267-1');
assert.equal(db2.config.sanitizeFilter, true);
await m.disconnect();
});
it('allows setting client on a disconnected connection (gh-9164)', async function() {
const client = await mongodb.MongoClient.connect(start.uri);
const conn = mongoose.createConnection().setClient(client);
assert.equal(conn.readyState, 1);
await conn.createCollection('test');
const res = await conn.dropCollection('test');
assert.ok(res);
await conn.close();
});
it('connection.asPromise() resolves to a connection instance (gh-9496)', async function() {
const m = new mongoose.Mongoose();
m.connect(start.uri);
const conn = await m.connection.asPromise();
assert.ok(conn instanceof m.Connection);
assert.ok(conn);
});
it('allows overwriting models (gh-9406)', function() {
const m = new mongoose.Mongoose();
const events = [];
m.connection.on('model', model => events.push(model));
const M1 = m.model('Test', Schema({ name: String }), null, { overwriteModels: true });
assert.equal(events.length, 1);
assert.equal(events[0], M1);
const M2 = m.model('Test', Schema({ name: String }), null, { overwriteModels: true });
assert.equal(events.length, 2);
assert.equal(events[1], M2);
const M3 = m.connection.model('Test', Schema({ name: String }), null, { overwriteModels: true });
assert.equal(events.length, 3);
assert.equal(events[2], M3);
assert.ok(M1 !== M2);
assert.ok(M2 !== M3);
assert.throws(() => m.model('Test', Schema({ name: String })), /overwrite/);
});
it('allows setting `overwriteModels` globally (gh-9406)', function() {
const m = new mongoose.Mongoose();
m.set('overwriteModels', true);
const M1 = m.model('Test', Schema({ name: String }));
const M2 = m.model('Test', Schema({ name: String }));
const M3 = m.connection.model('Test', Schema({ name: String }));
assert.ok(M1 !== M2);
assert.ok(M2 !== M3);
m.set('overwriteModels', false);
assert.throws(() => m.model('Test', Schema({ name: String })), /overwrite/);
});
describe('when connecting with a secondary read preference(gh-9374)', function() {
describe('mongoose.connect', function() {
it('forces autoIndex & autoCreate to be false if read preference is secondary or secondaryPreferred', async function() {
const m = new mongoose.Mongoose();
await m.connect(start.uri, { readPreference: 'secondary' });
assert.strictEqual(m.connection.get('autoIndex'), false);
assert.strictEqual(m.connection.get('autoCreate'), false);
assert.strictEqual(m.connection.get('autoIndex'), false);
assert.strictEqual(m.connection.get('autoCreate'), false);
await m.disconnect();
});
it('throws if options try to set autoIndex to true', function() {
const opts = {
autoIndex: true,
readPreference: 'secondary'
};
const err = new MongooseError(
'MongoDB prohibits index creation on connections that read from ' +
'non-primary replicas. Connections that set "readPreference" to "secondary" or ' +
'"secondaryPreferred" may not opt-in to the following connection options: ' +
'autoCreate, autoIndex'
);
const m = new mongoose.Mongoose();
assert.rejects(() => m.connect(start.uri, opts), err);
});
it('throws if options.config.autoIndex is true, even if options.autoIndex is false', function() {
const opts = {
readPreference: 'secondary',
autoIndex: false,
config: {
autoIndex: true
}
};
const err = new MongooseError(
'MongoDB prohibits index creation on connections that read from ' +
'non-primary replicas. Connections that set "readPreference" to "secondary" or ' +
'"secondaryPreferred" may not opt-in to the following connection options: ' +
'autoCreate, autoIndex'
);
const m = new mongoose.Mongoose();
assert.rejects(m.connect(start.uri, opts), err);
});
});
describe('mongoose.createConnection', function() {
it('forces autoIndex & autoCreate to be false if read preference is secondary or secondaryPreferred (gh-9374)', async function() {
const conn = new mongoose.createConnection(start.uri, { readPreference: 'secondary' });
assert.equal(conn.get('autoIndex'), false);
assert.equal(conn.get('autoCreate'), false);
const conn2 = new mongoose.createConnection(start.uri, { readPreference: 'secondaryPreferred' });
assert.equal(conn2.get('autoIndex'), false);
assert.equal(conn2.get('autoCreate'), false);
await conn.close();
await conn2.close();
});
it('keeps autoIndex & autoCreate as true by default if read preference is primaryPreferred (gh-9374)', async function() {
const conn = new mongoose.createConnection(start.uri, { readPreference: 'primaryPreferred' });
assert.equal(conn.get('autoIndex'), undefined);
assert.equal(conn.get('autoCreate'), undefined);
await conn.close();
});
it('throws if options try to set autoIndex to true', function() {
const opts = {
readPreference: 'secondary',
autoIndex: true
};
const err = new MongooseError(
'MongoDB prohibits index creation on connections that read from ' +
'non-primary replicas. Connections that set "readPreference" to "secondary" or ' +
'"secondaryPreferred" may not opt-in to the following connection options: ' +
'autoCreate, autoIndex'
);
const m = new mongoose.Mongoose();
return assert.rejects(
() => m.createConnection(start.uri, opts).asPromise(),
err
);
});
it('throws if options.config.autoIndex is true, even if options.autoIndex is false', async function() {
const opts = {
readPreference: 'secondary',
autoIndex: false,
config: {
autoIndex: true
}
};
const err = new MongooseError(
'MongoDB prohibits index creation on connections that read from ' +
'non-primary replicas. Connections that set "readPreference" to "secondary" or ' +
'"secondaryPreferred" may not opt-in to the following connection options: ' +
'autoCreate, autoIndex'
);
await assert.rejects(
() => mongoose.createConnection(start.uri, opts).asPromise(),
err
);
});
});
});
it('Connection id should be scoped per Mongoose Instance (gh-10025)', function() {
const m = new mongoose.Mongoose();
const conn = m.createConnection();
const m1 = new mongoose.Mongoose();
const conn2 = m1.createConnection();
const conn3 = m.createConnection();
assert.deepStrictEqual(m.connection.id, 0);
assert.deepStrictEqual(conn.id, m.connection.id + 1);
assert.deepStrictEqual(m1.connection.id, 0);
assert.deepStrictEqual(conn2.id, m1.connection.id + 1);
assert.deepStrictEqual(conn3.id, m.connection.id + 2);
});
describe('Automatic init', function() {
it('re-runs init() if connecting after disconnecting (gh-12047)', async function() {
const conn = await mongoose.createConnection(start.uri).asPromise();
const Test = conn.model('Test', new Schema({ name: { type: String, index: true } }));
await Test.init();
let indexes = await Test.collection.listIndexes().toArray();
assert.equal(indexes.length, 2);
assert.equal(indexes[1].name, 'name_1');
await conn.db.dropCollection(Test.collection.collectionName);
const err = await Test.collection.listIndexes().toArray().then(() => null, err => err);
assert.ok(err);
assert.equal(err.codeName, 'NamespaceNotFound');
await conn.close();
await conn.openUri(start.uri);
assert.ok(Test.$init);
await Test.init();
indexes = await Test.collection.listIndexes().toArray();
assert.equal(indexes.length, 2);
assert.equal(indexes[1].name, 'name_1');
await conn.close();
});
it('re-runs init() if running setClient() after disconnecting (gh-12047)', async function() {
const conn = await mongoose.createConnection(start.uri).asPromise();
const Test = conn.model('Test', new Schema({ name: { type: String, index: true } }));
await Test.init();
let indexes = await Test.collection.listIndexes().toArray();
assert.equal(indexes.length, 2);
assert.equal(indexes[1].name, 'name_1');
await conn.db.dropCollection(Test.collection.collectionName);
const err = await Test.collection.listIndexes().toArray().then(() => null, err => err);
assert.ok(err);
assert.equal(err.codeName, 'NamespaceNotFound');
await conn.close();
const client = await mongodb.MongoClient.connect(start.uri);
try {
conn.setClient(client);
assert.ok(Test.$init);
await Test.init();
indexes = await Test.collection.listIndexes().toArray();
assert.equal(indexes.length, 2);
assert.equal(indexes[1].name, 'name_1');
await client.close();
} catch (err) {
await client.close().catch(() => {});
throw err;
}
});
});
describe('Connection#syncIndexes() (gh-10893) (gh-11039)', () => {
let connection;
let mongooseInstance;
before(async() => {
mongooseInstance = new mongoose.Mongoose();
connection = await mongooseInstance.createConnection(start.uri).asPromise();
await connection.dropDatabase();
});
beforeEach(() => connection.deleteModel(/.*/));
afterEach(async() => {
await connection.dropDatabase();
});
after(async() => {
await connection.close();
});
it('Allows a syncIndexes option with connection mongoose.connection.syncIndexes (gh-10893)', async function() {
const coll = 'tests2';
let User = connection.model('Test', new Schema({ name: { type: String, index: true } }, { autoIndex: false }), coll);
const indexesAfterFirstSync = await connection.syncIndexes();
assert.deepEqual(indexesAfterFirstSync, { Test: [] });
const indexesAfterSecondSync = await User.listIndexes();
assert.deepEqual(
indexesAfterSecondSync.map(i => i.key),
[{ _id: 1 }, { name: 1 }]
);
connection.deleteModel(/Test/);
User = connection.model('Test', new Schema({
otherName: { type: String, index: true }
}, { autoIndex: false }), coll);
const indexesAfterDropping = await connection.syncIndexes();
assert.deepEqual(indexesAfterDropping, { Test: ['name_1'] });
});
it('does not sync indexes automatically when `autoIndex: true` (gh-11039)', async function() {
const buildingSchema = new Schema({ name: String }, { autoIndex: false });
buildingSchema.index({ name: 1 });
const Building = connection.model('Building', buildingSchema);
const floorSchema = new Schema({ name: String }, { autoIndex: false });
floorSchema.index({ name: 1 }, { unique: true });
const Floor = connection.model('Floor', floorSchema);
const officeSchema = new Schema({ name: String }, { autoIndex: false });
const Office = connection.model('Office', officeSchema);
await Promise.all([
Building.createCollection(),
Floor.createCollection(),
Office.createCollection()
]);
const [
buildingIndexes,
floorIndexes,
officeIndexes
] = await Promise.all([
Building.listIndexes(),
Floor.listIndexes(),
Office.listIndexes()
]);
assert.deepEqual(buildingIndexes.map(index => index.key), [{ _id: 1 }]);
assert.deepEqual(floorIndexes.map(index => index.key), [{ _id: 1 }]);
assert.deepEqual(officeIndexes.map(index => index.key), [{ _id: 1 }]);
});
it('stops as soon as one model fails with `continueOnError: false` (gh-11039)', async function() {
const buildingSchema = new Schema({ name: String }, { autoIndex: false });
buildingSchema.index({ name: 1 });
const Building = connection.model('Building', buildingSchema);
const floorSchema = new Schema({ name: String }, { autoIndex: false });
floorSchema.index({ name: 1 }, { unique: true });
const Floor = connection.model('Floor', floorSchema);
const officeSchema = new Schema({ name: String }, { autoIndex: false });
officeSchema.index({ name: 1 });
const Office = connection.model('Office', officeSchema);
await Floor.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
const err = await connection.syncIndexes().then(() => null, err => err);
assert.ok(err);
const [
buildingIndexes,
floorIndexes,
officeIndexes
] = await Promise.all([
Building.listIndexes(),
Floor.listIndexes(),
Office.listIndexes()
]);
assert.deepEqual(buildingIndexes.map(index => index.key), [{ _id: 1 }, { name: 1 }]);
assert.deepEqual(floorIndexes.map(index => index.key), [{ _id: 1 }]);
assert.deepEqual(officeIndexes.map(index => index.key), [{ _id: 1 }]);
});
it('error includes a property with all the errors when `continueOnError: false`', async() => {
const bookSchema = new Schema({ name: String }, { autoIndex: false });
bookSchema.index({ name: 1 }, { unique: true });
const Book = connection.model('Book', bookSchema);
await Book.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
const err = await connection.syncIndexes({ continueOnError: false }).then(() => null, err => err);
assert.ok(err);
assert.ok(err.message.includes('E11000'));
assert.equal(err.name, 'SyncIndexesError');
assert.equal(err.errors['Book'].code, 11000);
assert.equal(err.errors['Book'].code, 11000);
});
it('`continueOnError` is false by default', async() => {
const bookSchema = new Schema({ name: String }, { autoIndex: false });
bookSchema.index({ name: 1 }, { unique: true });
const Book = connection.model('Book', bookSchema);
await Book.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
const err = await connection.syncIndexes().then(() => null, err => err);
assert.ok(err);
assert.ok(err.message.includes('E11000'));
assert.equal(err.name, 'SyncIndexesError');
assert.equal(err.errors['Book'].code, 11000);
assert.equal(err.errors['Book'].code, 11000);
});
it('when `continueOnError: true` it will continue to sync indexes even if one model fails', async() => {
const buildingSchema = new Schema({ name: String }, { autoIndex: false });
buildingSchema.index({ name: 1 });
const Building = connection.model('Building', buildingSchema);
const floorSchema = new Schema({ name: String }, { autoIndex: false });
floorSchema.index({ name: 1 }, { unique: true });
const Floor = connection.model('Floor', floorSchema);
const officeSchema = new Schema({ name: String }, { autoIndex: false });
officeSchema.index({ name: 1 });
const Office = connection.model('Office', officeSchema);
await Floor.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
await connection.syncIndexes({ continueOnError: true });
const [
buildingIndexes,
floorIndexes,
officeIndexes
] = await Promise.all([
Building.listIndexes(),
Floor.listIndexes(),
Office.listIndexes()
]);
assert.deepEqual(buildingIndexes.map(index => index.key), [{ _id: 1 }, { name: 1 }]);
assert.deepEqual(floorIndexes.map(index => index.key), [{ _id: 1 }]);
assert.deepEqual(officeIndexes.map(index => index.key), [{ _id: 1 }, { name: 1 }]);
});
it('when `continueOnError: true` it will return a map of modelNames and their sync results/errors', async() => {
const buildingSchema = new Schema({ name: String }, { autoIndex: false });
buildingSchema.index({ name: 1 });
connection.model('Building', buildingSchema);
const floorSchema = new Schema({ name: String }, { autoIndex: false });
floorSchema.index({ name: 1 }, { unique: true });
const Floor = connection.model('Floor', floorSchema);
const officeSchema = new Schema({ name: String }, { autoIndex: false });
officeSchema.index({ name: 1 });
connection.model('Office', officeSchema);
await Floor.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
const result = await connection.syncIndexes({ continueOnError: true });
assert.ok(Array.isArray(result['Building']));
assert.ok(result['Floor'].name, 'MongoServerError');
assert.ok(Array.isArray(result['Office']));
});
it('mongoose.syncIndexes(...) accepts `continueOnError`', async() => {
const m = new mongoose.Mongoose();
await m.connect(start.uri);
const buildingSchema = new Schema({ name: String }, { autoIndex: false });
buildingSchema.index({ name: 1 });
m.model('Building', buildingSchema);
const floorSchema = new Schema({ name: String }, { autoIndex: false });
floorSchema.index({ name: 1 }, { unique: true });
const Floor = m.model('Floor', floorSchema);
const officeSchema = new Schema({ name: String }, { autoIndex: false });
officeSchema.index({ name: 1 });
m.model('Office', officeSchema);
await Floor.insertMany([
{ name: 'I am a duplicate' },
{ name: 'I am a duplicate' }
]);
const result = await m.syncIndexes({ continueOnError: true });
assert.ok(Array.isArray(result['Building']));
assert.ok(result['Floor'].name, 'MongoServerError');
assert.ok(Array.isArray(result['Office']));
await m.disconnect();
});
});
it('model() works with 1 argument and overwriteModels set to true (gh-12359)', function() {
const m = new mongoose.Mongoose();
m.set('overwriteModels', true);
const Test = m.model('Test', new Schema({ name: String }));
assert.equal(m.model('Test'), Test);
const Test2 = m.connection.model('Test2', new Schema({ name: String }));
assert.equal(m.connection.model('Test2'), Test2);
});
it('creates collection if creating model while connection is disconnected with bufferCommands=false', async function() {
const m = new mongoose.Mongoose();
m.set('bufferCommands', false);
const conn = await m.createConnection(start.uri, { bufferCommands: false }).asPromise();
conn.client.emit('serverDescriptionChanged', { newDescription: { type: 'Unknown' } });
const Test = conn.model('Test', new Schema({ name: String }));
const [res] = await Promise.all([
Test.findOne().exec(),
Promise.resolve(resolve => setTimeout(resolve, 100)).then(() => {
conn.client.emit('serverDescriptionChanged', { newDescription: { type: 'Single' } });
})
]);
assert.equal(res, null);
await m.disconnect();
});
it('should create connections with unique IDs also if one has been destroyed (gh-12966)', function() {
const m = new mongoose.Mongoose();
m.createConnection();
m.createConnection();
m.connections[0].destroy();
m.createConnection();
m.createConnection();
m.createConnection();
const connectionIds = m.connections.map(c => c.id);
assert.deepEqual(connectionIds, [1, 2, 3, 4, 5]);
});
it('should not create default connection with createInitialConnection = false (gh-12965)', function() {
const m = new mongoose.Mongoose({
createInitialConnection: false
});
assert.deepEqual(m.connections.length, 0);
});
it('with autoCreate = false after schema create (gh-12940)', async function() {
const m = new mongoose.Mongoose();
const schema = new Schema({ name: String }, {
collation: { locale: 'en_US', strength: 1 },
collection: 'gh12940_Conn'
});
const Model = m.model('gh12940_Conn', schema);
await m.connect(start.uri, {
autoCreate: false
});
await Model.init();
const res = await m.connection.db.listCollections().toArray();
assert.ok(!res.map(c => c.name).includes('gh12940_Conn'));
});
it('does not wait for buffering if autoCreate: false (gh-15241)', async function() {
const m = new mongoose.Mongoose();
m.set('bufferTimeoutMS', 100);
const schema = new Schema({ name: String }, {
autoCreate: false
});
const Model = m.model('gh15241_Conn', schema);
await Model.init();
});
it('should not create default connection with createInitialConnection = false (gh-12965)', function() {
const m = new mongoose.Mongoose({
createInitialConnection: false
});
assert.deepEqual(m.connections.length, 0);
});
it('should demonstrate the withSession() function (gh-14330)', async function() {
if (!process.env.REPLICA_SET && !process.env.START_REPLICA_SET) {
this.skip();
}
const m = new mongoose.Mongoose();
m.connect(start.uri);
let session = null;
await m.connection.withSession(s => {
session = s;
});
assert.ok(session);
});
it('listDatabases() should return a list of database objects with a name property (gh-9048)', async function() {
const connection = await mongoose.createConnection(start.uri).asPromise();
await connection.createCollection('tests').catch(() => {});
const { databases } = await connection.listDatabases();
assert.ok(connection.name);
assert.ok(databases.map(database => database.name).includes(connection.name));
});
describe('createCollections()', function() {
it('should create collections for all models on the connection with the createCollections() function (gh-13300)', async function() {
const m = new mongoose.Mongoose();
const schema = new Schema({ name: String });
const A = m.model('gh13300A', schema, 'gh13300A');
const B = m.model('gh13300B', schema, 'gh13300B');
const C = m.model('gh13300C', schema, 'gh13300C');
await m.connect(start.uri);
await m.connection.createCollections();
const collections = await m.connection.db.listCollections().toArray();
assert.equal(collections.length, 3);
const collectionNames = collections.map(inner => Object.values(inner)[0]);
assert.equal(collectionNames.includes(A.modelName), true);
assert.equal(collectionNames.includes(B.modelName), true);
assert.equal(collectionNames.includes(C.modelName), true);
});
});
describe('processConnectionOptions', function() {
let m = null;
after(async() => {
await m.disconnect();
});
it('should not throw an error when attempting to mutate unmutable options object gh-13335', async function() {
m = new mongoose.Mongoose();
const opts = Object.preventExtensions({ readPreference: 'secondaryPreferred' });
const conn = await m.connect(start.uri, opts);
assert.ok(conn);
});
});
it('connection bulkWrite() ordered (gh-15028)', async function() {
const db = start();
const version = await start.mongodVersion();
if (version[0] < 8) {
this.skip();
return;
}
const Test = db.model('Test', new Schema({ name: { type: String, required: true } }));
await Test.deleteMany({});
const res = await db.bulkWrite([{ model: 'Test', name: 'insertOne', document: { name: 'test1' } }]);
assert.equal(res.insertedCount, 1);
assert.ok(await Test.exists({ name: 'test1' }));
await db.bulkWrite([{ model: Test, name: 'insertOne', document: { name: 'test2' } }]);
assert.ok(await Test.exists({ name: 'test2' }));
await assert.rejects(
() => db.bulkWrite([{ name: 'insertOne', document: { name: 'foo' } }]),
/Must specify model in Connection.prototype.bulkWrite\(\) operations/
);
await assert.rejects(
() => db.bulkWrite([{ model: Test, document: { name: 'foo' } }]),
/Must specify operation name in Connection.prototype.bulkWrite\(\)/
);
await assert.rejects(
() => db.bulkWrite([{ model: Test, name: 'upsertAll', document: { name: 'foo' } }]),
/Unrecognized bulkWrite\(\) operation name upsertAll/
);
});
it('connection bulkWrite() unordered (gh-15028)', async function() {
const db = start();
const version = await start.mongodVersion();
if (version[0] < 8) {
this.skip();
return;
}
const Test = db.model('Test', new Schema({ name: { type: String, required: true }, num: Number }));
await Test.deleteMany({});
await db.bulkWrite([{ model: 'Test', name: 'insertOne', document: { name: 'test1' } }], { ordered: false });
assert.ok(await Test.exists({ name: 'test1' }));
await db.bulkWrite([{ model: Test, name: 'insertOne', document: { name: 'test2' } }], { ordered: false });
assert.ok(await Test.exists({ name: 'test2' }));
await assert.rejects(
() => {
return db.bulkWrite([
{ name: 'insertOne', document: { name: 'foo' } },
{ model: Test, name: 'insertOne', document: { name: 'test3' } }
], { ordered: false, throwOnValidationError: true });
},
/Must specify model in Connection.prototype.bulkWrite\(\) operations/
);
assert.ok(await Test.exists({ name: 'test3' }));
await assert.rejects(
() => db.bulkWrite([
{ model: Test, document: { name: 'foo' } },
{ model: Test, name: 'insertOne', document: { name: 'test4' } }
], { ordered: false, throwOnValidationError: true }),
/Must specify operation name in Connection.prototype.bulkWrite\(\)/
);
assert.ok(await Test.exists({ name: 'test4' }));
await assert.rejects(
() => db.bulkWrite([
{ model: Test, name: 'upsertAll', document: { name: 'foo' } },
{ model: Test, name: 'insertOne', document: { name: 'test5' } }
], { ordered: false, throwOnValidationError: true }),
/Unrecognized bulkWrite\(\) operation name upsertAll/
);
assert.ok(await Test.exists({ name: 'test5' }));
const res = await db.bulkWrite([
{ model: 'Test', name: 'updateOne', filter: { name: 'test5' }, update: { $set: { num: 42 } } },
{ model: 'Test', name: 'updateOne', filter: { name: 'test4' }, update: { $set: { num: 'not a number' } } }
], { ordered: false });
assert.equal(res.matchedCount, 1);
assert.equal(res.modifiedCount, 1);
assert.equal(res.mongoose.results.length, 2);
assert.equal(res.mongoose.results[0], null);
assert.ok(res.mongoose.results[1] instanceof CastError);
assert.ok(res.mongoose.results[1].message.includes('not a number'));
});
it('buffers connection helpers', async function() {
const m = new mongoose.Mongoose();
const promise = m.connection.listCollections();
await new Promise(resolve => setTimeout(resolve, 100));
await m.connect(start.uri, { bufferTimeoutMS: 1000 });
await promise;
await m.connection.listCollections();
await m.disconnect();
});
it('connection helpers buffering times out', async function() {
const m = new mongoose.Mongoose();
m.set('bufferTimeoutMS', 100);
await assert.rejects(m.connection.listCollections(), /Connection operation buffering timed out after 100ms/);
});
it('supports db-level aggregate on connection (gh-15118)', async function() {
const db = start();
const version = await start.mongodVersion();
if (version[0] < 6) {
this.skip();
return;
}
const result = await db.aggregate([
{ $documents: [{ x: 10 }, { x: 2 }, { x: 5 }] },
{ $bucketAuto: { groupBy: '$x', buckets: 4 } }
]);
assert.deepStrictEqual(result, [
{ _id: { min: 2, max: 5 }, count: 1 },
{ _id: { min: 5, max: 10 }, count: 1 },
{ _id: { min: 10, max: 10 }, count: 1 }
]);
const cursor = await db.aggregate([
{ $documents: [{ x: 10 }, { x: 2 }, { x: 5 }] },
{ $bucketAuto: { groupBy: '$x', buckets: 4 } }
]).cursor();
const cursorResult = [];
while (true) {
const doc = await cursor.next();
if (doc == null) {
break;
} else {
cursorResult.push(doc);
}
}
assert.deepStrictEqual(cursorResult, [
{ _id: { min: 2, max: 5 }, count: 1 },
{ _id: { min: 5, max: 10 }, count: 1 },
{ _id: { min: 10, max: 10 }, count: 1 }
]);
});
});