From 1babd2ffe9f92b6c7b011c9b4a1c28930e13ffa1 Mon Sep 17 00:00:00 2001 From: Krystian Jarmicki Date: Sun, 20 Dec 2020 13:14:32 +0100 Subject: [PATCH] prevent the module from throwing uncatchable errors --- lib/bulk.js | 8 ++++---- lib/collection.js | 37 +++++++++++++++++++------------------ lib/cursor.js | 16 ++++++++-------- lib/db.js | 8 ++++---- mongo-mock.js | 6 ++++-- test/mock.test.js | 30 +++++++++++++++++++++++++++++- 6 files changed, 68 insertions(+), 37 deletions(-) diff --git a/lib/bulk.js b/lib/bulk.js index a869375..acc4e1d 100644 --- a/lib/bulk.js +++ b/lib/bulk.js @@ -30,7 +30,7 @@ module.exports = function Bulk(collection, ordered) { //Runs operations only one at a time function executeOperations(operations, callback) { callback = arguments[arguments.length - 1]; - asyncish(() => { + asyncish().then(() => { operations.reduce((promiseChain, operation) => { return promiseChain.then(() => { executedOps.push(operation); @@ -41,7 +41,7 @@ module.exports = function Bulk(collection, ordered) { callback(null, executedOps); }) .catch(callback) - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { callback = function (e, r) { e ? reject(e) : resolve(r) }; @@ -58,13 +58,13 @@ module.exports = function Bulk(collection, ordered) { promises.push(operation.fnc.apply(this, operation.args)); } - asyncish(() => { + asyncish().then(() => { Promise.all(promises) .then((operations) => { callback(null, operations) }) .catch(callback); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { diff --git a/lib/collection.js b/lib/collection.js index e5d457c..43e8ee6 100644 --- a/lib/collection.js +++ b/lib/collection.js @@ -60,8 +60,9 @@ module.exports = function Collection(db, state) { debug('deleteMany %j', filter); const opts = find_options(arguments); - asyncish(function () { + asyncish().then(function () { cursor(state.documents || [], opts).toArray((err, docsToRemove) => { + if (err) return callback(err); debug('docs', docsToRemove); if (docsToRemove.length) { debug(state.documents.length); @@ -76,7 +77,7 @@ module.exports = function Collection(db, state) { } callback(null, { result: { n: docsToRemove.length, ok: 1 }, deletedCount: docsToRemove.length, connection: db }); }); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function(resolve, reject) { @@ -89,7 +90,7 @@ module.exports = function Collection(db, state) { debug('deleteOne %j', filter); - asyncish(function () { + asyncish().then(function () { var deletionIndex = _.findIndex(state.documents || [], filter); var docs = deletionIndex === -1 ? [] : state.documents.splice(deletionIndex, 1); @@ -98,7 +99,7 @@ module.exports = function Collection(db, state) { state.persist(); } callback(null, { result: { n: docs.length, ok: 1 }, deletedCount: docs.length, connection: db }); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { callback = function (e, r) { e ? reject(e) : resolve(r) }; @@ -121,9 +122,9 @@ module.exports = function Collection(db, state) { if (!opts.callback) return crsr; - asyncish(function () { + asyncish().then(function () { opts.callback(null, crsr); - }); + }).catch(e => callback(e)); }, findAndModify: NotImplemented, findAndRemove: NotImplemented, @@ -138,7 +139,7 @@ module.exports = function Collection(db, state) { crsr.next().then(function (doc) { opts.callback(null, doc); - }); + }).catch(e => callback(e)); }, findOneAndDelete: NotImplemented, findOneAndReplace: NotImplemented, @@ -168,7 +169,7 @@ module.exports = function Collection(db, state) { callback(null, findResult); }); }) - .catch(callback); + .catch(e => callback(e)); if (typeof callback!=='function') { return new Promise(function (resolve,reject) { @@ -204,7 +205,7 @@ module.exports = function Collection(db, state) { //The observed behavior of `mongodb` is that documents // are committed until the first error. No information // about the successful inserts are return :/ - asyncish(function () { + asyncish().then(function () { var insertedIds = []; for (var i = 0; i < docs.length; i++) { var doc = docs[i]; @@ -230,7 +231,7 @@ module.exports = function Collection(db, state) { connection: {}, ops: _.cloneDeepWith(docs, cloneObjectIDs) }); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { callback = function (e, r) { e ? reject(e) : resolve(r) }; @@ -275,14 +276,14 @@ module.exports = function Collection(db, state) { debug('remove %j', selector); - asyncish(function () { + asyncish().then(function () { var docs = _.remove(state.documents || [], selector); if (docs.length) { if (debug.enabled) debug("removed: " + docs.map(function (doc) { return doc._id; })); state.persist(); } callback(null, {result:{n:docs.length,ok:1}, ops:docs, connection:db}); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { callback = function (e, r) { e ? reject(e) : resolve(r) }; @@ -312,7 +313,7 @@ module.exports = function Collection(db, state) { var action = (options.upsert?"upsert: ":"update: "); debug('%s.%s %j', name, action, selector); - asyncish(function () { + asyncish().then(function () { var docs = state.documents || []; if (options.multi) { docs = docs.filter(sift(selector)) @@ -354,7 +355,7 @@ module.exports = function Collection(db, state) { state.persist(); callback(null, opResult); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { @@ -381,7 +382,7 @@ module.exports = function Collection(db, state) { var action = (options.upsert ? "upsert: " : "update: "); debug('%s.%s %j', name, action, selector); - asyncish(function () { + asyncish().then(function () { var docs = (state.documents || []).filter(sift(selector)); if (!Array.isArray(docs)) docs = [docs]; debug('%s.%s %j', name, action, docs); @@ -417,7 +418,7 @@ module.exports = function Collection(db, state) { state.persist(); callback(null, opResult); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { @@ -444,7 +445,7 @@ module.exports = function Collection(db, state) { var action = (options.upsert ? "upsert: " : "update: "); debug('%s.%s %j', name, action, selector); - asyncish(function () { + asyncish().then(function () { var docs = first(selector, state.documents || []) || []; if (!Array.isArray(docs)) docs = [docs]; debug('%s.%s %j', name, action, docs); @@ -480,7 +481,7 @@ module.exports = function Collection(db, state) { state.persist(); callback(null, opResult); - }); + }).catch(e => callback(e)); if (typeof callback !== 'function') { return new Promise(function (resolve, reject) { diff --git a/lib/cursor.js b/lib/cursor.js index 92448be..c3f4a9c 100644 --- a/lib/cursor.js +++ b/lib/cursor.js @@ -75,9 +75,9 @@ var Cursor = module.exports = function(documents, opts) { if(typeof callback !== 'function') return Promise.resolve(getDocs(applySkipLimit).length); - asyncish(function () { + asyncish().then(function () { callback(null, getDocs(applySkipLimit).length) - }); + }).catch(callback); }, project: function (toProject) { @@ -109,9 +109,9 @@ var Cursor = module.exports = function(documents, opts) { if(typeof callback !== 'function') return Promise.resolve(doc); - asyncish(function () { + asyncish().then(function () { callback(null, doc); - }); + }).catch(callback); }, _triggerStream: function (filteredDocuments) { @@ -163,9 +163,9 @@ var Cursor = module.exports = function(documents, opts) { if(!callback) return Promise.resolve(done()); - asyncish(function () { + asyncish().then(function () { callback(null, done()) - }); + }).catch(callback); }, forEach: function (iterator, callback) { @@ -182,9 +182,9 @@ var Cursor = module.exports = function(documents, opts) { if(!callback) return Promise.resolve(done()); - asyncish(function () { + asyncish().then(function () { callback(null, done()) - }); + }).catch(callback); }, on: function (event, fn) { diff --git a/lib/db.js b/lib/db.js index e87f347..90dfc39 100644 --- a/lib/db.js +++ b/lib/db.js @@ -54,7 +54,7 @@ var Db = module.exports = function(dbname, server) { if(typeof options !== 'object') options = {}; debug('createCollection("%s")', name); - asyncish(function () { + asyncish().then(function () { var collection = getCollection(name); if(collection.documents) { debug('createCollection("%s") - collection exists', name); @@ -67,7 +67,7 @@ var Db = module.exports = function(dbname, server) { collection.persist(options.autoIndexId); callback(null, collection); - }); + }).catch(e => callback(e)); if(typeof callback!=='function') { return new Promise(function (resolve) { @@ -167,14 +167,14 @@ var Db = module.exports = function(dbname, server) { }, logout: NotImplemented, open: function(callback) { - asyncish(function () { + asyncish().then(function () { if(!open) { debug('%s open', dbname); //keep the process running like a live connection would open = setInterval(function () {}, 600000); } callback(null, iface); - }); + }).catch(e => callback(e)); if(typeof callback!=='function') { return new Promise(function (resolve) { callback = function (e, r) { resolve(r) }; diff --git a/mongo-mock.js b/mongo-mock.js index 059da2f..c79b57a 100644 --- a/mongo-mock.js +++ b/mongo-mock.js @@ -4,8 +4,10 @@ module.exports = { get max_delay() { return delay; }, set max_delay(n) { delay = Number(n) || 0; }, // pretend we are doing things async - asyncish: function asyncish(callback) { - setTimeout(callback, Math.random()*(delay)); + asyncish: function asyncish() { + return new Promise(function (resolve) { + setTimeout(resolve, Math.random()*(delay)); + }); }, get find_options() { return require('./lib/find_options.js') }, get MongoClient() { return require('./lib/mongo_client.js') }, diff --git a/test/mock.test.js b/test/mock.test.js index e55cdde..ae6d6cd 100644 --- a/test/mock.test.js +++ b/test/mock.test.js @@ -24,7 +24,6 @@ describe('mock tests', function () { connected_db.close().then(done).catch(done) }); - describe('databases', function() { it('should list collections', function(done) { var listCollectionName = "test_databases_listCollections_collection"; @@ -1381,4 +1380,33 @@ describe('mock tests', function () { }); }); }); + + describe('entire module', function () { + before(function (done) { + collection.insertOne({ + items: [ + { + description: 'a description' + } + ] + }, done); + }); + + after(function (done) { + collection.remove({}, done); + }); + + it('should not throw uncatchable errors', function (done) { + // this call should make ModifyJs throw an error + collection.findOneAndUpdate( + { 'items.description': 'a description' }, + { $set: { 'items.$.description': 'a new description' } }, + function (err) { + if (!err) return done(new Error('Expected to catch an error')); + err.message.should.equal('The positional operator did not find the match needed from the query'); + done(); + } + ); + }); + }); });