diff --git a/README.md b/README.md index 6b6498c..bbe0350 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,31 @@ Version > 1.0.x is a major rewrite of mongojs. So expect some things not to work * __Removed__ `mongojs.connect` use `mongojs()` directly instead +## Async/Await Support + +To have mongojs behave even more like the official MongoDB repl, rather than use callbacks, you can query or update the database using async/await. This works wherever callbacks can be used. Just omit the callback and use the await keyword. Don't forget to use the "async" keyword on your caller function. + +```js + +(async function(){ // you need an async caller. + + // insert a record in mycollection + await db.mycollection.insert({hello: 'world'}) + + // update it + await db.mycollection.update({$set: {hello: 'again'}}) + + // find everything. when not providing a callback, collection.find() returns a cursor. This is similar to how the MongoDB Shell iterates 20 records at a time. To get all results immediately, call toArray() + var docs = await db.mycollection.find().toArray() // docs is an array of all the documents in mycollection + + // find a document using a native ObjectId + var doc = db.mycollection.findOne({_id: mongojs.ObjectId('523209c4561c640000000001')} + // and so on, see earlier examples. + +})() + +``` + # API This API documentation is a work in progress. diff --git a/lib/collection.js b/lib/collection.js index 6efd923..e55c35f 100644 --- a/lib/collection.js +++ b/lib/collection.js @@ -4,10 +4,12 @@ var xtend = require('xtend') var Cursor = require('./cursor') var Bulk = require('./bulk') // TODO: Make this configurable by users -var writeOpts = {writeConcern: {w: 1}, ordered: true} -var noop = function () {} +var writeOpts = { writeConcern: { w: 1 }, ordered: true } var oid = mongodb.ObjectID.createPk +var promisify = require('./promisify') +var noop = promisify.noop + var Collection = function (opts, getConnection) { this._name = opts.name this._getConnection = getConnection @@ -15,7 +17,9 @@ var Collection = function (opts, getConnection) { var collectionName = this._name this._getConnection(function (err, connection) { - if (err) { return cb(err) } + if (err) { + return cb(err) + } cb(null, connection.collection(collectionName)) }) @@ -24,18 +28,20 @@ var Collection = function (opts, getConnection) { Collection.prototype.find = function (query, projection, opts, cb) { if (typeof query === 'function') return this.find({}, null, null, query) - if (typeof projection === 'function') return this.find(query, null, null, projection) - if (typeof opts === 'function') return this.find(query, projection, null, opts) + if (typeof projection === 'function') { return this.find(query, null, null, projection) } + if (typeof opts === 'function') { return this.find(query, projection, null, opts) } var self = this function getCursor (cb) { self._getCollection(function (err, collection) { - if (err) { return cb(err) } + if (err) { + return cb(err) + } // projection is now an option on find if (projection) { if (opts) opts.projection = projection - else opts = {projection: projection} + else opts = { projection: projection } } cb(null, collection.find(query, opts)) }) @@ -43,23 +49,28 @@ Collection.prototype.find = function (query, projection, opts, cb) { var cursor = new Cursor(getCursor) - if (cb) return cursor.toArray(cb) + if (cb) { + return cursor.toArray(cb) + } return cursor } Collection.prototype.findOne = function (query, projection, cb) { if (typeof query === 'function') return this.findOne({}, null, query) - if (typeof projection === 'function') return this.findOne(query, null, projection) - this.find(query, projection).next(function (err, doc) { - if (err) return cb(err) - cb(null, doc) - }) + if (typeof projection === 'function') { return this.findOne(query, null, projection) } + return promisify((cb) => { + var cursor = this.find(query, projection).next(function (err, doc) { + if (err) return cb(err) + cb(null, doc) + }) + return cursor + }, cb) } Collection.prototype.findAndModify = function (opts, cb) { - this.runCommand('findAndModify', opts, function (err, result) { + return this.runCommand('findAndModify', opts, function (err, result) { if (err) return cb(err) - cb(null, result.value, result.lastErrorObject || {n: 0}) + cb(null, result.value, result.lastErrorObject || { n: 0 }) }) } @@ -69,7 +80,10 @@ Collection.prototype.count = function (query, cb) { } Collection.prototype.distinct = function (field, query, cb) { - this.runCommand('distinct', {key: field, query: query}, function (err, result) { + return this.runCommand('distinct', { key: field, query: query }, function ( + err, + result + ) { if (err) return cb(err) cb(null, result.values) }) @@ -79,22 +93,23 @@ Collection.prototype.insert = function (docOrDocs, opts, cb) { if (!opts && !cb) return this.insert(docOrDocs, {}, noop) if (typeof opts === 'function') return this.insert(docOrDocs, {}, opts) if (opts && !cb) return this.insert(docOrDocs, opts, noop) + return promisify(cb => { + this._getCollection(function (err, collection) { + if (err) return cb(err) - this._getCollection(function (err, collection) { - if (err) return cb(err) - - var docs = Array.isArray(docOrDocs) ? docOrDocs : [docOrDocs] - for (var i = 0; i < docs.length; i++) { - if (!docs[i]._id) docs[i]._id = oid() - } + var docs = Array.isArray(docOrDocs) ? docOrDocs : [docOrDocs] + for (var i = 0; i < docs.length; i++) { + if (!docs[i]._id) docs[i]._id = oid() + } - collection.insert(docs, xtend(writeOpts, opts), function (err) { - if (err) return cb(err) - // TODO: Add a test for this - is this really not needed anymore? - // if (res && res.result && res.result.writeErrors && res.result.writeErrors.length > 0) return cb(res.result.writeErrors[0]) - cb(null, docOrDocs) + collection.insert(docs, xtend(writeOpts, opts), function (err) { + if (err) return cb(err) + // TODO: Add a test for this - is this really not needed anymore? + // if (res && res.result && res.result.writeErrors && res.result.writeErrors.length > 0) return cb(res.result.writeErrors[0]) + cb(null, docOrDocs) + }) }) - }) + }, cb) } Collection.prototype.update = function (query, update, opts, cb) { @@ -102,14 +117,23 @@ Collection.prototype.update = function (query, update, opts, cb) { if (typeof opts === 'function') return this.update(query, update, {}, opts) cb = cb || noop - this._getCollection(function (err, collection) { - if (err) return cb(err) + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) - collection.update(query, update, xtend(writeOpts, opts), function (err, result) { - if (err) { return cb(err) } - cb(null, result.result) + collection.update(query, update, xtend(writeOpts, opts), function ( + err, + result + ) { + if (err) { + return cb(err) + } + cb(null, result.result) + }) }) - }) + }, + cb + ) } Collection.prototype.save = function (doc, opts, cb) { @@ -118,33 +142,45 @@ Collection.prototype.save = function (doc, opts, cb) { if (!cb) return this.save(doc, opts, noop) if (doc._id) { - this.update({_id: doc._id}, doc, xtend({upsert: true}, opts), function (err) { - if (err) return cb(err) - cb(null, doc) - }) + return promisify((cb) => { + return this.update( + { _id: doc._id }, + doc, + xtend({ upsert: true }, opts), + function (err) { + if (err) return cb(err) + cb(null, doc) + } + ) + }, cb) } else { - this.insert(doc, opts, cb) + return this.insert(doc, opts, cb) } } Collection.prototype.remove = function (query, opts, cb) { - if (typeof query === 'function') return this.remove({}, {justOne: false}, query) - if (typeof opts === 'function') return this.remove(query, {justOne: false}, opts) - if (typeof opts === 'boolean') return this.remove(query, {justOne: opts}, cb) - if (!opts) return this.remove(query, {justOne: false}, cb) + if (typeof query === 'function') { return this.remove({}, { justOne: false }, query) } + if (typeof opts === 'function') { return this.remove(query, { justOne: false }, opts) } + if (typeof opts === 'boolean') { return this.remove(query, { justOne: opts }, cb) } + if (!opts) return this.remove(query, { justOne: false }, cb) if (!cb) return this.remove(query, opts, noop) - this._getCollection(function (err, collection) { - if (err) return cb(err) + return promisify(cb => { + this._getCollection(function (err, collection) { + if (err) return cb(err) - var deleteOperation = opts.justOne ? 'deleteOne' : 'deleteMany' + var deleteOperation = opts.justOne ? 'deleteOne' : 'deleteMany' - collection[deleteOperation](query, xtend(writeOpts, opts), function (err, result) { - if (err) return cb(err) - result.result.deletedCount = result.deletedCount - cb(null, result.result) + collection[deleteOperation](query, xtend(writeOpts, opts), function ( + err, + result + ) { + if (err) return cb(err) + result.result.deletedCount = result.deletedCount + cb(null, result.result) + }) }) - }) + }, cb) } Collection.prototype.rename = function (name, opts, cb) { @@ -152,29 +188,36 @@ Collection.prototype.rename = function (name, opts, cb) { if (!opts) return this.rename(name, {}, noop) if (!cb) return this.rename(name, noop) - this._getCollection(function (err, collection) { - if (err) return cb(err) - collection.rename(name, opts, cb) - }) + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) + collection.rename(name, opts, cb) + }) + }, cb) } Collection.prototype.drop = function (cb) { - this.runCommand('drop', cb) + return this.runCommand('drop', cb) } Collection.prototype.stats = function (cb) { - this.runCommand('collStats', cb) + return this.runCommand('collStats', cb) } Collection.prototype.mapReduce = function (map, reduce, opts, cb) { - if (typeof opts === 'function') { return this.mapReduce(map, reduce, {}, opts) } - if (!cb) { return this.mapReduce(map, reduce, opts, noop) } - - this._getCollection(function (err, collection) { - if (err) return cb(err) + if (typeof opts === 'function') { + return this.mapReduce(map, reduce, {}, opts) + } + if (!cb) { + return this.mapReduce(map, reduce, opts, noop) + } + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) - collection.mapReduce(map, reduce, opts, cb) - }) + collection.mapReduce(map, reduce, opts, cb) + }) + }, cb) } Collection.prototype.runCommand = function (cmd, opts, cb) { @@ -186,10 +229,12 @@ Collection.prototype.runCommand = function (cmd, opts, cb) { Object.keys(opts).forEach(function (key) { cmdObject[key] = opts[key] }) - this._getConnection(function (err, connection) { - if (err) return cb(err) - connection.command(cmdObject, cb) - }) + return promisify((cb) => { + this._getConnection(function (err, connection) { + if (err) return cb(err) + connection.command(cmdObject, cb) + }) + }, cb) } Collection.prototype.toString = function () { @@ -197,11 +242,11 @@ Collection.prototype.toString = function () { } Collection.prototype.dropIndexes = function (cb) { - this.runCommand('dropIndexes', {index: '*'}, cb) + return this.runCommand('dropIndexes', { index: '*' }, cb) } Collection.prototype.dropIndex = function (index, cb) { - this.runCommand('dropIndexes', {index: index}, cb) + return this.runCommand('dropIndexes', { index: index }, cb) } Collection.prototype.createIndex = function (index, opts, cb) { @@ -209,11 +254,13 @@ Collection.prototype.createIndex = function (index, opts, cb) { if (!opts) return this.createIndex(index, {}, noop) if (!cb) return this.createIndex(index, opts, noop) - this._getCollection(function (err, collection) { - if (err) return cb(err) + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) - collection.createIndex(index, opts, cb) - }) + collection.createIndex(index, opts, cb) + }) + }, cb) } Collection.prototype.ensureIndex = function (index, opts, cb) { @@ -221,38 +268,56 @@ Collection.prototype.ensureIndex = function (index, opts, cb) { if (!opts) return this.ensureIndex(index, {}, noop) if (!cb) return this.ensureIndex(index, opts, noop) - this._getCollection(function (err, collection) { - if (err) return cb(err) - - collection.ensureIndex(index, opts, cb) - }) + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) + collection.ensureIndex(index, opts, cb) + }) + }, cb) } Collection.prototype.getIndexes = function (cb) { - this._getCollection(function (err, collection) { - if (err) { return cb(err) } + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) { + return cb(err) + } - collection.indexes(cb) - }) + collection.indexes(cb) + }) + }, cb) } Collection.prototype.reIndex = function (cb) { - this.runCommand('reIndex', cb) + return this.runCommand('reIndex', cb) } Collection.prototype.isCapped = function (cb) { - this._getCollection(function (err, collection) { - if (err) { return cb(err) } + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) { + return cb(err) + } - collection.isCapped(cb) - }) + collection.isCapped(cb) + }) + }, cb) } Collection.prototype.group = function (doc, cb) { - this._getCollection(function (err, collection) { - if (err) return cb(err) - collection.group(doc.key || doc.keyf, doc.cond, doc.initial, doc.reduce, doc.finalize, cb) - }) + return promisify((cb) => { + this._getCollection(function (err, collection) { + if (err) return cb(err) + collection.group( + doc.key || doc.keyf, + doc.cond, + doc.initial, + doc.reduce, + doc.finalize, + cb + ) + }) + }, cb) } Collection.prototype.aggregate = function () { @@ -264,7 +329,10 @@ Collection.prototype.aggregate = function () { cb = once(pipeline.pop()) } - if ((pipeline.length === 1 || pipeline.length === 2) && Array.isArray(pipeline[0])) { + if ( + (pipeline.length === 1 || pipeline.length === 2) && + Array.isArray(pipeline[0]) + ) { opts = pipeline[1] pipeline = pipeline[0] } diff --git a/lib/cursor.js b/lib/cursor.js index d30609d..197488b 100644 --- a/lib/cursor.js +++ b/lib/cursor.js @@ -1,6 +1,7 @@ var util = require('util') var thunky = require('thunky') var Readable = require('readable-stream').Readable +var promisify = require('./promisify') try { var hooks = require('async_hooks') @@ -38,18 +39,23 @@ Cursor.prototype.next = function (cb) { var self = this - this._get(function (err, cursor) { - if (err) return cb(err) - - if (cursor.cursorState.dead || cursor.cursorState.killed) { - destroy(self) - return cb(null, null) - } else { - cursor.next(cb) - } - }) + let maybePromise = promisify((cb) => { + this._get(function (err, cursor) { + if (err) return cb(err) - return this + if (cursor.cursorState.dead || cursor.cursorState.killed) { + destroy(self) + return cb(null, null) + } else { + cursor.next(cb) + } + }) + }, cb) + if (cb) { + return this + } else { + return maybePromise // it is in fact a promise here. + } } Cursor.prototype.rewind = function (cb) { @@ -67,36 +73,39 @@ Cursor.prototype.toArray = function (cb) { var array = [] var self = this - var loop = function () { - self.next(function (err, obj) { - if (err) return cb(err) - if (!obj) return cb(null, array) - array.push(obj) + return promisify((cb) => { + var loop = function () { + self.next(function (err, obj) { + if (err) return cb(err) + if (!obj) return cb(null, array) + array.push(obj) - // Fix for #270 RangeError: Maximum call stack size exceeded using Collection.find - setImmediate(loop) - }) - } + // Fix for #270 RangeError: Maximum call stack size exceeded using Collection.find + setImmediate(loop) + }) + } - loop() + loop() + }, cb) } Cursor.prototype.map = function (mapfn, cb) { var array = [] var self = this - - var loop = function () { - self.next(function (err, obj) { - if (err) return cb(err) - if (!obj) return cb(null, array) - array.push(mapfn(obj)) + return promisify((cb) => { + var loop = function () { + self.next(function (err, obj) { + if (err) return cb(err) + if (!obj) return cb(null, array) + array.push(mapfn(obj)) // Fix for #270 RangeError: Maximum call stack size exceeded using Collection.find - setImmediate(loop) - }) - } + setImmediate(loop) + }) + } - loop() + loop() + }, cb) } Cursor.prototype.forEach = function (fn) { @@ -132,35 +141,42 @@ Cursor.prototype.count = function (cb) { if (this._hook) cb = wrapHook(this, cb) var self = this - - this._get(function (err, cursor) { - if (err) { return cb(err) } - cursor.count(false, self.opts, cb) - }) + return promisify((cb) => { + this._get(function (err, cursor) { + if (err) { return cb(err) } + cursor.count(false, self.opts, cb) + }) + }, cb) } Cursor.prototype.size = function (cb) { if (this._hook) cb = wrapHook(this, cb) var self = this - - this._get(function (err, cursor) { - if (err) { return cb(err) } - cursor.count(true, self.opts, cb) - }) + return promisify((cb) => { + this._get(function (err, cursor) { + if (err) { return cb(err) } + cursor.count(true, self.opts, cb) + }) + }, cb) } Cursor.prototype.explain = function (cb) { + // TODO: look at what the hook will do with promises, and if that's desired. if (this._hook) cb = wrapHook(this, cb) - this._get(function (err, cursor) { - if (err) { return cb(err) } - cursor.explain(cb) - }) + return promisify((cb) => { + this._get(function (err, cursor) { + if (err) { return cb(err) } + cursor.explain(cb) + }) + }, cb) } Cursor.prototype.destroy = function () { + // TODO: Promisify var self = this + this._get(function (err, cursor) { if (err) return done(err) if (cursor.close) { @@ -195,7 +211,8 @@ function runInAsyncScope (self, cb, err, val) { self._hook.runInAsyncScope(cb, null, err, val) } else { self._hook.emitBefore() - cb(err, val) + // A callback may not exist during async/await usage. + cb && cb(err, val) self._hook.emitAfter() } } diff --git a/lib/database.js b/lib/database.js index 54fa2ab..676e14f 100644 --- a/lib/database.js +++ b/lib/database.js @@ -5,8 +5,9 @@ var thunky = require('thunky') var parse = require('parse-mongo-url') var util = require('util') var EventEmitter = require('events').EventEmitter +var promisify = require('./promisify') -var noop = function () {} +var noop = promisify.noop var Database = function (connString, cols, options) { var self = this @@ -81,14 +82,16 @@ Database.prototype.close = function (force, cb) { var self = this cb = cb || noop - this._getConnection(function (err, server, conn) { - if (err) return cb(err) + return promisify((cb) => { + this._getConnection(function (err, server, conn) { + if (err) return cb(err) - conn.close(force) + conn.close(force) - self.emit('close') - cb() - }) + self.emit('close') + cb() + }) + }, cb) } Database.prototype.runCommand = function (opts, cb) { @@ -99,24 +102,28 @@ Database.prototype.runCommand = function (opts, cb) { opts[tmp] = 1 } - this._getConnection(function (err, connection) { - if (err) return cb(err) - connection.command(opts, function (err, result) { + return promisify((cb) => { + this._getConnection(function (err, connection) { if (err) return cb(err) - cb(null, result) + connection.command(opts, function (err, result) { + if (err) return cb(err) + cb(null, result) + }) }) - }) + }, cb) } Database.prototype.listCollections = function (cb) { - this._getConnection(function (err, connection) { - if (err) { return cb(err) } - - connection.listCollections().toArray(function (err, collections) { + return promisify((cb) => { + this._getConnection(function (err, connection) { if (err) { return cb(err) } - cb(null, collections) + + connection.listCollections().toArray(function (err, collections) { + if (err) { return cb(err) } + cb(null, collections) + }) }) - }) + }, cb) } Database.prototype.getCollectionNames = function (cb) { @@ -133,33 +140,33 @@ Database.prototype.createCollection = function (name, opts, cb) { Object.keys(opts).forEach(function (opt) { cmd[opt] = opts[opt] }) - this.runCommand(cmd, cb) + return this.runCommand(cmd, cb) } Database.prototype.stats = function (scale, cb) { if (typeof scale === 'function') return this.stats(1, scale) - this.runCommand({dbStats: 1, scale: scale}, cb) + return this.runCommand({dbStats: 1, scale: scale}, cb) } Database.prototype.dropDatabase = function (cb) { - this.runCommand('dropDatabase', cb) + return this.runCommand('dropDatabase', cb) } Database.prototype.createUser = function (usr, cb) { var cmd = xtend({createUser: usr.user}, usr) delete cmd.user - this.runCommand(cmd, cb) + return this.runCommand(cmd, cb) } Database.prototype.addUser = Database.prototype.createUser Database.prototype.dropUser = function (username, cb) { - this.runCommand({dropUser: username}, cb) + return this.runCommand({dropUser: username}, cb) } Database.prototype.removeUser = Database.prototype.dropUser Database.prototype.eval = function (fn) { var cb = arguments[arguments.length - 1] - this.runCommand({ + return this.runCommand({ eval: fn.toString(), args: Array.prototype.slice.call(arguments, 1, arguments.length - 1) }, function (err, res) { @@ -169,11 +176,11 @@ Database.prototype.eval = function (fn) { } Database.prototype.getLastErrorObj = function (cb) { - this.runCommand('getLastError', cb) + return this.runCommand('getLastError', cb) } Database.prototype.getLastError = function (cb) { - this.runCommand('getLastError', function (err, res) { + return this.runCommand('getLastError', function (err, res) { if (err) return cb(err) cb(null, res.err) }) diff --git a/lib/promisify.js b/lib/promisify.js new file mode 100644 index 0000000..5e479c4 --- /dev/null +++ b/lib/promisify.js @@ -0,0 +1,26 @@ +/** + * promisify(fn, cb) + * + * A progressive promisifier. Wraps an async code block which normally takes a callback, and allows + * instead returning a promise when no callback is passed in. + * + * fn - function which accepts a callback + * cb - a callback to use when the wrapped code finishes, omitting this, promisify returns a Promise. + */ +module.exports = function (fn, cb) { + if (cb === module.exports.noop || !cb) { + var promise = new Promise(function (resolve, reject) { + let cbp = (err, result) => { + promise._done = true // just for testing. + if (err) reject(err) + resolve(result) + } + fn(cbp) + }) + return promise + } else { + return fn(cb) + } +} + +module.exports.noop = function () { } diff --git a/test/tape.js b/test/tape.js index 7a12fb5..ee2eb67 100644 --- a/test/tape.js +++ b/test/tape.js @@ -1,5 +1,4 @@ var test = require('tape') - var wait = global.setImmediate || process.nextTick wait(function () { @@ -9,4 +8,10 @@ wait(function () { }) }) +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason) + // Stack Trace + console.log(reason.stack) + process.exit(1) +}) module.exports = test diff --git a/test/test-bulk-replace-one-promise.js b/test/test-bulk-replace-one-promise.js new file mode 100644 index 0000000..b2ca124 --- /dev/null +++ b/test/test-bulk-replace-one-promise.js @@ -0,0 +1,28 @@ +var insert = require('./insert') + +insert('bulk replace one promise', [{ + name: 'Squirtle', type: 'water' +}, { + name: 'Starmie', type: 'water' +}], async function (db, t, done) { + var resp = await db.runCommand('serverStatus') + if (parseFloat(resp.version) < 2.6) return t.end() + + var bulk = db.a.initializeUnorderedBulkOp() + bulk.find({ name: 'Squirtle' }).replaceOne({ name: 'Charmander', type: 'fire' }) + bulk.find({ name: 'Starmie' }).replaceOne({ type: 'fire' }) + + bulk.execute(function (err, res) { + t.error(err) + t.ok(res.ok) + db.a.find(function (err, res) { + t.error(err) + t.equal(res[0].name, 'Charmander') + t.equal(res[1].name, undefined) + + t.equal(res[0].type, 'fire') + t.equal(res[1].type, 'fire') + t.end() + }) + }) +}) diff --git a/test/test-find-by-objectid-promise.js b/test/test-find-by-objectid-promise.js new file mode 100644 index 0000000..41f5d2b --- /dev/null +++ b/test/test-find-by-objectid-promise.js @@ -0,0 +1,13 @@ +var insert = require('./insert') +var mongojs = require('../') + +insert('find by ObjectId', [{ + hello: 'world' +}], async function (db, t, done) { + let docs = await db.a.find({_id: db.ObjectId('abeabeabeabeabeabeabeabe')}, {hello: 1}).toArray() + t.equal(docs.length, 0) + await db.a.save({_id: mongojs.ObjectId('abeabeabeabeabeabeabeabe')}) + docs = await db.a.find({_id: db.ObjectId('abeabeabeabeabeabeabeabe')}, {hello: 1}).toArray() + t.equal(docs.length, 1) + done() +}) diff --git a/test/test-find-one-promise.js b/test/test-find-one-promise.js new file mode 100644 index 0000000..352d996 --- /dev/null +++ b/test/test-find-one-promise.js @@ -0,0 +1,12 @@ +var insert = require('./insert') + +insert('findOne', [{ + hello: 'world1' +}, { + hello: 'world2' +}], async function (db, t, done) { + let doc = await db.a.findOne() + t.equal(typeof doc, 'object') + t.ok(doc.hello === 'world1' || doc.hello === 'world2') + done() +}) diff --git a/test/test-find-promise.js b/test/test-find-promise.js new file mode 100644 index 0000000..87b99ef --- /dev/null +++ b/test/test-find-promise.js @@ -0,0 +1,10 @@ +var insert = require('./insert') + +insert('find promise', [{ + hello: 'world' +}], async function (db, t, done) { + let docs = await db.a.find().toArray() + t.equal(docs.length, 1) + t.equal(docs[0].hello, 'world') + done() +}) diff --git a/test/test-insert-promise.js b/test/test-insert-promise.js new file mode 100644 index 0000000..0f88d56 --- /dev/null +++ b/test/test-insert-promise.js @@ -0,0 +1,24 @@ +var test = require('./tape') +var mongojs = require('../index') +var db = mongojs('test', ['a', 'b']) + +test('insert promise', async function (t) { + let docs = await db.a.insert( + [{ name: 'Squirtle' }, { name: 'Charmander' }, { name: 'Bulbasaur' }]) + t.ok(docs[0]._id) + t.ok(docs[1]._id) + t.ok(docs[2]._id) + + // It should only return one document in the + // callback when one document is passed instead of an array + let doc = await db.a.insert({ name: 'Lapras' }) + t.equal(doc.name, 'Lapras') + + // If you pass a one element array the callback should + // have a one element array + let docs2 = await db.a.insert([{ name: 'Pidgeotto' }]) + t.equal(docs2[0].name, 'Pidgeotto') + t.equal(docs2.length, 1) + await db.a.remove() + db.close(t.end.bind(t)) +}) diff --git a/test/test-noop-promise-cleanup.js b/test/test-noop-promise-cleanup.js new file mode 100644 index 0000000..eb03c81 --- /dev/null +++ b/test/test-noop-promise-cleanup.js @@ -0,0 +1,27 @@ +var test = require('./tape') +var mongojs = require('../index') +var db = mongojs('test', ['a', 'b']) + +test('insert', function (t) { + db.a.insert([{name: 'Squirtle'}, {name: 'Charmander'}, {name: 'Bulbasaur'}], function (err, docs) { + t.error(err) + t.ok(docs[0]._id) + t.ok(docs[1]._id) + t.ok(docs[2]._id) + + // Ensure we old calls with "noop" (no callback) still happen as expected. + // And that promises are cleaned up. + var ignoredPromise = db.a.insert([{name: 'Pidgeotto'}]) + setTimeout(function () { + db.a.find({name: 'Pidgeotto'}, function (err, docs) { + t.error(err) + t.equal(docs[0].name, 'Pidgeotto') + t.equal(docs.length, 1) + t.ok(ignoredPromise._done) + db.a.remove(function () { + db.close(t.end.bind(t)) + }) + }) + }, 200) + }) +}) diff --git a/test/test-optional-callback-promise.js b/test/test-optional-callback-promise.js new file mode 100644 index 0000000..e9728e3 --- /dev/null +++ b/test/test-optional-callback-promise.js @@ -0,0 +1,12 @@ +var test = require('./tape') +var mongojs = require('../index') +var db = mongojs('test', ['a', 'b']) + +test('optional callback promise', function (t) { + db.a.ensureIndex({hello: 1}) + setTimeout(async function () { + await db.a.count() + await db.close() + t.end() + }, 100) +}) diff --git a/test/test-optional-callback.js b/test/test-optional-callback.js index 07a9907..b0fc3fd 100644 --- a/test/test-optional-callback.js +++ b/test/test-optional-callback.js @@ -3,7 +3,7 @@ var mongojs = require('../index') var db = mongojs('test', ['a', 'b']) test('optional callback', function (t) { - db.a.ensureIndex({hello: 'world'}) + db.a.ensureIndex({hello: 1}) setTimeout(function () { db.a.count(function () { db.close(t.end.bind(t)) diff --git a/test/test-save-promise.js b/test/test-save-promise.js new file mode 100644 index 0000000..4e2b86a --- /dev/null +++ b/test/test-save-promise.js @@ -0,0 +1,18 @@ +var test = require('./tape') +var mongojs = require('../index') +var db = mongojs('test', ['a', 'b']) + +test('save promise', async function (t) { + let doc = await db.a.save({hello: 'world'}) + t.equal(doc.hello, 'world') + t.ok(doc._id) + + doc.hello = 'verden' + doc = await db.a.save(doc) + t.ok(doc._id) + t.equal(doc.hello, 'verden') + await db.a.remove() + + await db.close() + t.end() +}) diff --git a/test/test-update-promise.js b/test/test-update-promise.js new file mode 100644 index 0000000..5fd56cc --- /dev/null +++ b/test/test-update-promise.js @@ -0,0 +1,13 @@ +var insert = require('./insert') + +insert('update promise', [{ + hello: 'world' +}], async function (db, t, done) { + let info = await db.a.update({hello: 'world'}, {$set: {hello: 'verden'}}) + + t.equal(info.n, 1) + + let doc = await db.a.findOne() + t.equal(doc.hello, 'verden') + done() +})