diff --git a/packages/minimongo/minimongo.js b/packages/minimongo/minimongo.js index 54f634565a..5bee132f17 100644 --- a/packages/minimongo/minimongo.js +++ b/packages/minimongo/minimongo.js @@ -637,7 +637,7 @@ LocalCollection.prototype.upsert = function (selector, mod, options, callback) { return self.update(selector, mod, _.extend({}, options, { upsert: true, _returnObject: true - }, callback)); + }), callback); }; LocalCollection.prototype._modifyAndNotify = function ( diff --git a/packages/mongo-livedata/mongo_driver.js b/packages/mongo-livedata/mongo_driver.js index e48adff121..86708242e3 100644 --- a/packages/mongo-livedata/mongo_driver.js +++ b/packages/mongo-livedata/mongo_driver.js @@ -489,6 +489,9 @@ _.each(["insert", "update", "remove"], function (method) { }; }); +// XXX MongoConnection.upsert() does not return the id of the inserted document +// unless you set it explicitly in the selector or modifier (as a replacement +// doc). MongoConnection.prototype.upsert = function (collectionName, selector, mod, options, callback) { var self = this; @@ -501,7 +504,7 @@ MongoConnection.prototype.upsert = function (collectionName, selector, mod, _.extend({}, options, { upsert: true, _returnObject: true - }, callback)); + }), callback); }; MongoConnection.prototype.find = function (collectionName, selector, options) { diff --git a/packages/mongo-livedata/mongo_livedata_tests.js b/packages/mongo-livedata/mongo_livedata_tests.js index 9fc5f80acb..33877d6218 100644 --- a/packages/mongo-livedata/mongo_livedata_tests.js +++ b/packages/mongo-livedata/mongo_livedata_tests.js @@ -30,8 +30,8 @@ var stripId = function (obj) { delete obj._id; }; -var compareResults = function (test, useUpdate, actual, expected) { - if (useUpdate) { +var compareResults = function (test, skipIds, actual, expected) { + if (skipIds) { _.map(actual, stripId); _.map(expected, stripId); } @@ -1057,291 +1057,343 @@ if (Meteor.isServer) { // over the network. _.each(Meteor.isServer ? [true, false] : [true], function (minimongo) { _.each([true, false], function (useUpdate) { - Tinytest.add("mongo-livedata - " + (useUpdate ? "update " : "") + "upsert" + (minimongo ? " minimongo" : "") + ", " + idGeneration, function (test) { - var run = test.runId(); - var options = collectionOptions; - if (minimongo) - options = _.extend({}, collectionOptions, { connection: null }); - var coll = new Meteor.Collection("livedata_upsert_collection_"+run, options); + _.each([true, false], function (useDirectCollection) { + Tinytest.add("mongo-livedata - " + (useUpdate ? "update " : "") + "upsert" + (minimongo ? " minimongo" : "") + (useDirectCollection ? " direct collection " : "") + ", " + idGeneration, function (test) { + var run = test.runId(); + var options = collectionOptions; + // We don't get ids back when we use update() to upsert, or when we are + // directly calling MongoConnection.upsert(). + var skipIds = useUpdate || (! minimongo && useDirectCollection); + if (minimongo) + options = _.extend({}, collectionOptions, { connection: null }); + var coll = new Meteor.Collection( + "livedata_upsert_collection_"+run+ + (useUpdate ? "_update_" : "") + + (minimongo ? "_minimongo_" : "") + + (useDirectCollection ? "_direct_" : "") + "", + options + ); + if (useDirectCollection) + coll = coll._collection; - var result1 = upsert(coll, useUpdate, {foo: 'bar'}, {foo: 'bar'}); - test.equal(result1.numberAffected, 1); - if (! useUpdate) - test.isTrue(result1.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'bar', _id: result1.insertedId}]); + var result1 = upsert(coll, useUpdate, {foo: 'bar'}, {foo: 'bar'}); + test.equal(result1.numberAffected, 1); + if (! skipIds) + test.isTrue(result1.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{foo: 'bar', _id: result1.insertedId}]); - var result2 = upsert(coll, useUpdate, {foo: 'bar'}, {foo: 'baz'}); - test.equal(result2.numberAffected, 1); - if (! useUpdate) - test.isFalse(result2.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'baz', _id: result1.insertedId}]); + var result2 = upsert(coll, useUpdate, {foo: 'bar'}, {foo: 'baz'}); + test.equal(result2.numberAffected, 1); + if (! skipIds) + test.isFalse(result2.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{foo: 'baz', _id: result1.insertedId}]); - coll.remove({}); + coll.remove({}); - // Test values that require transformation to go into Mongo: + // Test values that require transformation to go into Mongo: - var t1 = new Meteor.Collection.ObjectID(); - var t2 = new Meteor.Collection.ObjectID(); - var result3 = upsert(coll, useUpdate, {foo: t1}, {foo: t1}); - test.equal(result3.numberAffected, 1); - if (! useUpdate) - test.isTrue(result3.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: t1, _id: result3.insertedId}]); + var t1 = new Meteor.Collection.ObjectID(); + var t2 = new Meteor.Collection.ObjectID(); + var result3 = upsert(coll, useUpdate, {foo: t1}, {foo: t1}); + test.equal(result3.numberAffected, 1); + if (! skipIds) + test.isTrue(result3.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{foo: t1, _id: result3.insertedId}]); - var result4 = upsert(coll, useUpdate, {foo: t1}, {foo: t2}); - test.equal(result2.numberAffected, 1); - if (! useUpdate) - test.isFalse(result2.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: t2, _id: result3.insertedId}]); + var result4 = upsert(coll, useUpdate, {foo: t1}, {foo: t2}); + test.equal(result2.numberAffected, 1); + if (! skipIds) + test.isFalse(result2.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{foo: t2, _id: result3.insertedId}]); - coll.remove({}); + coll.remove({}); - // Test modification by upsert + // Test modification by upsert - var result5 = upsert(coll, useUpdate, {name: 'David'}, {$set: {foo: 1}}); - test.equal(result5.numberAffected, 1); - if (! useUpdate) - test.isTrue(result5.insertedId); - var davidId = result5.insertedId; - compareResults(test, useUpdate, coll.find().fetch(), [{name: 'David', foo: 1, _id: davidId}]); + var result5 = upsert(coll, useUpdate, {name: 'David'}, {$set: {foo: 1}}); + test.equal(result5.numberAffected, 1); + if (! skipIds) + test.isTrue(result5.insertedId); + var davidId = result5.insertedId; + compareResults(test, skipIds, coll.find().fetch(), [{name: 'David', foo: 1, _id: davidId}]); - test.throws(function () { - // test that bad modifier fails fast - upsert(coll, useUpdate, {name: 'David'}, {$blah: {foo: 2}}); + test.throws(function () { + // test that bad modifier fails fast + upsert(coll, useUpdate, {name: 'David'}, {$blah: {foo: 2}}); + }); + + + var result6 = upsert(coll, useUpdate, {name: 'David'}, {$set: {foo: 2}}); + test.equal(result6.numberAffected, 1); + if (! skipIds) + test.isFalse(result6.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{name: 'David', foo: 2, + _id: result5.insertedId}]); + + var emilyId = coll.insert({name: 'Emily', foo: 2}); + compareResults(test, skipIds, coll.find().fetch(), [{name: 'David', foo: 2, _id: davidId}, + {name: 'Emily', foo: 2, _id: emilyId}]); + + // multi update by upsert + var result7 = upsert(coll, useUpdate, {foo: 2}, + {$set: {bar: 7}, + $setOnInsert: {name: 'Fred', foo: 2}}, + {multi: true}); + test.equal(result7.numberAffected, 2); + if (! skipIds) + test.isFalse(result7.insertedId); + compareResults(test, skipIds, coll.find().fetch(), [{name: 'David', foo: 2, bar: 7, _id: davidId}, + {name: 'Emily', foo: 2, bar: 7, _id: emilyId}]); + + // insert by multi upsert + var result8 = upsert(coll, useUpdate, {foo: 3}, + {$set: {bar: 7}, + $setOnInsert: {name: 'Fred', foo: 2}}, + {multi: true}); + test.equal(result8.numberAffected, 1); + if (! skipIds) + test.isTrue(result8.insertedId); + var fredId = result8.insertedId; + compareResults(test, skipIds, coll.find().fetch(), + [{name: 'David', foo: 2, bar: 7, _id: davidId}, + {name: 'Emily', foo: 2, bar: 7, _id: emilyId}, + {name: 'Fred', foo: 2, bar: 7, _id: fredId}]); + + // test `insertedId` option + var result9 = upsert(coll, useUpdate, {name: 'Steve'}, + {name: 'Steve'}, + {insertedId: 'steve'}); + test.equal(result9.numberAffected, 1); + if (! skipIds) + test.equal(result9.insertedId, 'steve'); + compareResults(test, skipIds, coll.find().fetch(), + [{name: 'David', foo: 2, bar: 7, _id: davidId}, + {name: 'Emily', foo: 2, bar: 7, _id: emilyId}, + {name: 'Fred', foo: 2, bar: 7, _id: fredId}, + {name: 'Steve', _id: 'steve'}]); + test.isTrue(coll.findOne('steve')); + test.isFalse(coll.findOne('fred')); + + // Test $ operator in selectors. + + var result10 = upsert(coll, useUpdate, + {$or: [{name: 'David'}, {name: 'Emily'}]}, + {$set: {foo: 3}}, {multi: true}); + test.equal(result10.numberAffected, 2); + if (! skipIds) + test.isFalse(result10.insertedId); + compareResults(test, skipIds, + [coll.findOne({name: 'David'}), coll.findOne({name: 'Emily'})], + [{name: 'David', foo: 3, bar: 7, _id: davidId}, + {name: 'Emily', foo: 3, bar: 7, _id: emilyId}] + ); + + var result11 = upsert( + coll, useUpdate, + { + name: 'Charlie', + $or: [{ foo: 2}, { bar: 7 }] + }, + { $set: { foo: 3 } } + ); + test.equal(result11.numberAffected, 1); + if (! skipIds) + test.isTrue(result11.insertedId); + var charlieId = result11.insertedId; + compareResults(test, skipIds, + coll.find({ name: 'Charlie' }).fetch(), + [{name: 'Charlie', foo: 3, _id: charlieId}]); }); - - - var result6 = upsert(coll, useUpdate, {name: 'David'}, {$set: {foo: 2}}); - test.equal(result6.numberAffected, 1); - if (! useUpdate) - test.isFalse(result6.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{name: 'David', foo: 2, - _id: result5.insertedId}]); - - var emilyId = coll.insert({name: 'Emily', foo: 2}); - compareResults(test, useUpdate, coll.find().fetch(), [{name: 'David', foo: 2, _id: davidId}, - {name: 'Emily', foo: 2, _id: emilyId}]); - - // multi update by upsert - var result7 = upsert(coll, useUpdate, {foo: 2}, - {$set: {bar: 7}, - $setOnInsert: {name: 'Fred', foo: 2}}, - {multi: true}); - test.equal(result7.numberAffected, 2); - if (! useUpdate) - test.isFalse(result7.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{name: 'David', foo: 2, bar: 7, _id: davidId}, - {name: 'Emily', foo: 2, bar: 7, _id: emilyId}]); - - // insert by multi upsert - var result8 = upsert(coll, useUpdate, {foo: 3}, - {$set: {bar: 7}, - $setOnInsert: {name: 'Fred', foo: 2}}, - {multi: true}); - test.equal(result8.numberAffected, 1); - if (! useUpdate) - test.isTrue(result8.insertedId); - var fredId = result8.insertedId; - compareResults(test, useUpdate, coll.find().fetch(), - [{name: 'David', foo: 2, bar: 7, _id: davidId}, - {name: 'Emily', foo: 2, bar: 7, _id: emilyId}, - {name: 'Fred', foo: 2, bar: 7, _id: fredId}]); - - // test `insertedId` option - var result9 = upsert(coll, useUpdate, {name: 'Steve'}, - {name: 'Steve'}, - {insertedId: 'steve'}); - test.equal(result9.numberAffected, 1); - if (! useUpdate) - test.equal(result9.insertedId, 'steve'); - compareResults(test, useUpdate, coll.find().fetch(), - [{name: 'David', foo: 2, bar: 7, _id: davidId}, - {name: 'Emily', foo: 2, bar: 7, _id: emilyId}, - {name: 'Fred', foo: 2, bar: 7, _id: fredId}, - {name: 'Steve', _id: 'steve'}]); - test.isTrue(coll.findOne('steve')); - test.isFalse(coll.findOne('fred')); - - // Test $ operator in selectors. - - var result10 = upsert(coll, useUpdate, - {$or: [{name: 'David'}, {name: 'Emily'}]}, - {$set: {foo: 3}}, {multi: true}); - test.equal(result10.numberAffected, 2); - if (! useUpdate) - test.isFalse(result10.insertedId); - compareResults(test, useUpdate, - [coll.findOne({name: 'David'}), coll.findOne({name: 'Emily'})], - [{name: 'David', foo: 3, bar: 7, _id: davidId}, - {name: 'Emily', foo: 3, bar: 7, _id: emilyId}] - ); - - var result11 = upsert( - coll, useUpdate, - { - name: 'Charlie', - $or: [{ foo: 2}, { bar: 7 }] - }, - { $set: { foo: 3 } } - ); - test.equal(result11.numberAffected, 1); - if (! useUpdate) - test.isTrue(result11.insertedId); - var charlieId = result11.insertedId; - compareResults(test, useUpdate, - coll.find({ name: 'Charlie' }).fetch(), - [{name: 'Charlie', foo: 3, _id: charlieId}]); }); }); }); +var asyncUpsertTestName = function (useNetwork, useDirectCollection, + useUpdate, idGeneration) { + return "mongo-livedata - async " + + (useUpdate ? "update " : "") + + "upsert " + + (useNetwork ? "over network " : "") + + (useDirectCollection ? ", direct collection " : "") + + idGeneration; +}; + // This is a duplicate of the test above, with some changes to make it work for -// async upserts that go over the network. -if (Meteor.isClient) { - _.each([true, false], function (useUpdate) { - Tinytest.addAsync("mongo-livedata - async " + (useUpdate ? "update " : "") + "upsert" + ", " + idGeneration, function (test, onComplete) { - var coll; - var run = test.runId(); - var collName = "livedata_upsert_collection_"+run; - Meteor.call("createInsecureCollection", collName, collectionOptions); - coll = new Meteor.Collection(collName, collectionOptions); - Meteor.subscribe("c-" + collName); - - var result1; - var next1 = function (err, result) { - result1 = result; - test.equal(result1.numberAffected, 1); - if (! useUpdate) { - test.isTrue(result1.insertedId); - test.equal(result1.insertedId, 'foo'); +// callback style. On the client, we test server-backed and in-memory +// collections, and run the tests for both the Meteor.Collection and the +// LocalCollection. On the server, we test mongo-backed collections, for both +// the Meteor.Collection and the MongoConnection. +_.each(Meteor.isServer ? [false] : [true, false], function (useNetwork) { + _.each(useNetwork ? [false] : [true, false], function (useDirectCollection) { + _.each([true, false], function (useUpdate) { + Tinytest.addAsync(asyncUpsertTestName(useNetwork, useDirectCollection, useUpdate, idGeneration), function (test, onComplete) { + var coll; + var run = test.runId(); + var collName = "livedata_upsert_collection_"+run+ + (useUpdate ? "_update_" : "") + + (useNetwork ? "_network_" : "") + + (useDirectCollection ? "_direct_" : ""); + if (useNetwork) { + Meteor.call("createInsecureCollection", collName, collectionOptions); + coll = new Meteor.Collection(collName, collectionOptions); + Meteor.subscribe("c-" + collName); + } else { + var opts = _.clone(collectionOptions); + if (Meteor.isClient) + opts.connection = null; + coll = new Meteor.Collection(collName, opts); + if (useDirectCollection) + coll = coll._collection; } - compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'bar', _id: 'foo'}]); - upsert(coll, useUpdate, {_id: 'foo'}, {foo: 'baz'}, next2); - }; - // Test starts here. - upsert(coll, useUpdate, {_id: 'foo'}, {_id: 'foo', foo: 'bar'}, next1); + var result1; + var next1 = function (err, result) { + result1 = result; + test.equal(result1.numberAffected, 1); + if (! useUpdate) { + test.isTrue(result1.insertedId); + test.equal(result1.insertedId, 'foo'); + } + compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'bar', _id: 'foo'}]); + upsert(coll, useUpdate, {_id: 'foo'}, {foo: 'baz'}, next2); + }; - var t1, t2, result2; - var next2 = function (err, result) { - result2 = result; - test.equal(result2.numberAffected, 1); - if (! useUpdate) - test.isFalse(result2.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'baz', _id: result1.insertedId}]); - coll.remove({_id: 'foo'}); - compareResults(test, useUpdate, coll.find().fetch(), []); + // Test starts here. + upsert(coll, useUpdate, {_id: 'foo'}, {_id: 'foo', foo: 'bar'}, next1); - // Test values that require transformation to go into Mongo: + var t1, t2, result2; + var next2 = function (err, result) { + result2 = result; + test.equal(result2.numberAffected, 1); + if (! useUpdate) + test.isFalse(result2.insertedId); + compareResults(test, useUpdate, coll.find().fetch(), [{foo: 'baz', _id: result1.insertedId}]); + coll.remove({_id: 'foo'}); + compareResults(test, useUpdate, coll.find().fetch(), []); - t1 = new Meteor.Collection.ObjectID(); - t2 = new Meteor.Collection.ObjectID(); - upsert(coll, useUpdate, {_id: t1}, {_id: t1, foo: 'bar'}, next3); - }; + // Test values that require transformation to go into Mongo: - var result3; - var next3 = function (err, result) { - result3 = result; - test.equal(result3.numberAffected, 1); - if (! useUpdate) { - test.isTrue(result3.insertedId); - test.equal(t1, result3.insertedId); - } - compareResults(test, useUpdate, coll.find().fetch(), [{_id: t1, foo: 'bar'}]); + t1 = new Meteor.Collection.ObjectID(); + t2 = new Meteor.Collection.ObjectID(); + upsert(coll, useUpdate, {_id: t1}, {_id: t1, foo: 'bar'}, next3); + }; - upsert(coll, useUpdate, {_id: t1}, {foo: t2}, next4); - }; + var result3; + var next3 = function (err, result) { + result3 = result; + test.equal(result3.numberAffected, 1); + if (! useUpdate) { + test.isTrue(result3.insertedId); + test.equal(t1, result3.insertedId); + } + compareResults(test, useUpdate, coll.find().fetch(), [{_id: t1, foo: 'bar'}]); - var next4 = function (err, result4) { - test.equal(result2.numberAffected, 1); - if (! useUpdate) - test.isFalse(result2.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{foo: t2, _id: result3.insertedId}]); + upsert(coll, useUpdate, {_id: t1}, {foo: t2}, next4); + }; - coll.remove({_id: t1}); + var next4 = function (err, result4) { + test.equal(result2.numberAffected, 1); + if (! useUpdate) + test.isFalse(result2.insertedId); + compareResults(test, useUpdate, coll.find().fetch(), [{foo: t2, _id: result3.insertedId}]); - // Test modification by upsert - upsert(coll, useUpdate, {_id: 'David'}, {$set: {foo: 1}}, next5); - }; + coll.remove({_id: t1}); - var result5; - var next5 = function (err, result) { - result5 = result; - test.equal(result5.numberAffected, 1); - if (! useUpdate) { - test.isTrue(result5.insertedId); - test.equal(result5.insertedId, 'David'); - } - var davidId = result5.insertedId; - compareResults(test, useUpdate, coll.find().fetch(), [{foo: 1, _id: davidId}]); + // Test modification by upsert + upsert(coll, useUpdate, {_id: 'David'}, {$set: {foo: 1}}, next5); + }; - // test that bad modifier fails - // The stub throws an exception about the invalid modifier, which - // livedata logs (so we suppress it). - Meteor._suppress_log(1); - upsert(coll, useUpdate, {_id: 'David'}, {$blah: {foo: 2}}, function (err) { - test.isTrue(err); - upsert(coll, useUpdate, {_id: 'David'}, {$set: {foo: 2}}, next6); - }); - }; + var result5; + var next5 = function (err, result) { + result5 = result; + test.equal(result5.numberAffected, 1); + if (! useUpdate) { + test.isTrue(result5.insertedId); + test.equal(result5.insertedId, 'David'); + } + var davidId = result5.insertedId; + compareResults(test, useUpdate, coll.find().fetch(), [{foo: 1, _id: davidId}]); - var result6; - var next6 = function (err, result) { - result6 = result; - test.equal(result6.numberAffected, 1); - if (! useUpdate) - test.isFalse(result6.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}]); + if (! Meteor.isClient && useDirectCollection) { + // test that bad modifier fails + // The stub throws an exception about the invalid modifier, which + // livedata logs (so we suppress it). + Meteor._suppress_log(1); + upsert(coll, useUpdate, {_id: 'David'}, {$blah: {foo: 2}}, function (err) { + if (! (Meteor.isClient && useDirectCollection)) + test.isTrue(err); + upsert(coll, useUpdate, {_id: 'David'}, {$set: {foo: 2}}, next6); + }); + } else { + // XXX skip this test for now for LocalCollection; the fact that + // we're in a nested sequence of callbacks means we're inside a + // Meteor.defer, which means the exception just gets + // logged. Something should be done about this at some point? Maybe + // LocalCollection callbacks don't really have to be deferred. + upsert(coll, useUpdate, {_id: 'David'}, {$set: {foo: 2}}, next6); + } + }; - var emilyId = coll.insert({_id: 'Emily', foo: 2}); - compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}, - {_id: 'Emily', foo: 2}]); + var result6; + var next6 = function (err, result) { + result6 = result; + test.equal(result6.numberAffected, 1); + if (! useUpdate) + test.isFalse(result6.insertedId); + compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}]); - // multi update by upsert. - // We can't actually update multiple documents since we have to do it by - // id, but at least make sure the multi flag doesn't mess anything up. - upsert(coll, useUpdate, {_id: 'Emily'}, - {$set: {bar: 7}, - $setOnInsert: {name: 'Fred', foo: 2}}, - {multi: true}, next7); - }; + var emilyId = coll.insert({_id: 'Emily', foo: 2}); + compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}, + {_id: 'Emily', foo: 2}]); - var result7; - var next7 = function (err, result) { - result7 = result; - test.equal(result7.numberAffected, 1); - if (! useUpdate) - test.isFalse(result7.insertedId); - compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}, - {_id: 'Emily', foo: 2, bar: 7}]); + // multi update by upsert. + // We can't actually update multiple documents since we have to do it by + // id, but at least make sure the multi flag doesn't mess anything up. + upsert(coll, useUpdate, {_id: 'Emily'}, + {$set: {bar: 7}, + $setOnInsert: {name: 'Fred', foo: 2}}, + {multi: true}, next7); + }; - // insert by multi upsert - upsert(coll, useUpdate, {_id: 'Fred'}, - {$set: {bar: 7}, - $setOnInsert: {name: 'Fred', foo: 2}}, - {multi: true}, next8); + var result7; + var next7 = function (err, result) { + result7 = result; + test.equal(result7.numberAffected, 1); + if (! useUpdate) + test.isFalse(result7.insertedId); + compareResults(test, useUpdate, coll.find().fetch(), [{_id: 'David', foo: 2}, + {_id: 'Emily', foo: 2, bar: 7}]); - }; + // insert by multi upsert + upsert(coll, useUpdate, {_id: 'Fred'}, + {$set: {bar: 7}, + $setOnInsert: {name: 'Fred', foo: 2}}, + {multi: true}, next8); - var result8; - var next8 = function (err, result) { - result8 = result; + }; - test.equal(result8.numberAffected, 1); - if (! useUpdate) { - test.isTrue(result8.insertedId); - test.equal(result8.insertedId, 'Fred'); - } - var fredId = result8.insertedId; - compareResults(test, useUpdate, coll.find().fetch(), - [{_id: 'David', foo: 2}, - {_id: 'Emily', foo: 2, bar: 7}, - {name: 'Fred', foo: 2, bar: 7, _id: fredId}]); - onComplete(); - }; + var result8; + var next8 = function (err, result) { + result8 = result; + + test.equal(result8.numberAffected, 1); + if (! useUpdate) { + test.isTrue(result8.insertedId); + test.equal(result8.insertedId, 'Fred'); + } + var fredId = result8.insertedId; + compareResults(test, useUpdate, coll.find().fetch(), + [{_id: 'David', foo: 2}, + {_id: 'Emily', foo: 2, bar: 7}, + {name: 'Fred', foo: 2, bar: 7, _id: fredId}]); + onComplete(); + }; + }); }); }); +}); +if (Meteor.isClient) { Tinytest.addAsync("mongo-livedata - async update/remove return values over network " + idGeneration, function (test, onComplete) { var coll; var run = test.runId(); @@ -1372,8 +1424,7 @@ if (Meteor.isClient) { }); }); }); - -} // end isClient +} // Runs a method and its stub which do some upserts. The method throws an error // if we don't get the right return values.