diff --git a/packages/mongo-livedata/collection.js b/packages/mongo-livedata/collection.js index b85257d20a..a3752f2963 100644 --- a/packages/mongo-livedata/collection.js +++ b/packages/mongo-livedata/collection.js @@ -366,8 +366,13 @@ _.each(["insert", "update", "remove"], function (name) { var insertId; var ret; - if (args.length && args[args.length - 1] instanceof Function) + // Pull off any callback (or perhaps a 'callback' variable that was passed + // in undefined, like how 'upsert' does it). + if (args.length && + (args[args.length - 1] === undefined || + args[args.length - 1] instanceof Function)) { callback = args.pop(); + } if (name === "insert") { if (!args.length) @@ -711,6 +716,16 @@ Meteor.Collection.prototype._defineMutationMethods = function() { if (generatedId !== null) args[0]._id = generatedId; // In insecure mode, allow any mutation (with a simple selector). + // XXX This is kind of bogus. Instead of blindly passing whatever + // we get from the network to this function, we should actually + // know the correct arguments for the function and pass just + // them. For example, if you have an extraneous extra null + // argument and this is Mongo on the server, the _wrapAsync'd + // functions like update will get confused and pass the + // "fut.resolver()" in the wrong slot, where _update will never + // invoke it. Bam, broken DDP connection. Probably should just + // take this whole method and write it three times, invoking + // helpers for the common code. return self._collection[method].apply(self._collection, args); } else { // In secure mode, if we haven't called allow or deny, then nothing diff --git a/packages/mongo-livedata/mongo_livedata_tests.js b/packages/mongo-livedata/mongo_livedata_tests.js index 2a9675aef8..eadd3f7176 100644 --- a/packages/mongo-livedata/mongo_livedata_tests.js +++ b/packages/mongo-livedata/mongo_livedata_tests.js @@ -1267,6 +1267,26 @@ testAsyncMulti('mongo-livedata - empty documents, ' + idGeneration, [ } ]); +// Regression test for #2413. +testAsyncMulti('mongo-livedata - upsert without callback, ' + idGeneration, [ + function (test, expect) { + this.collectionName = Random.id(); + if (Meteor.isClient) { + Meteor.call('createInsecureCollection', this.collectionName); + Meteor.subscribe('c-' + this.collectionName, expect()); + } + }, function (test, expect) { + var coll = new Meteor.Collection(this.collectionName, collectionOptions); + + // No callback! Before fixing #2413, this method never returned and + // so no future DDP methods worked either. + coll.upsert('foo', {bar: 1}); + // Do something else on the same method and expect it to actually work. + // (If the bug comes back, this will 'async batch timeout'.) + coll.insert({}, expect(function(){})); + } +]); + // See https://github.com/meteor/meteor/issues/594. testAsyncMulti('mongo-livedata - document with length, ' + idGeneration, [ function (test, expect) {