summaryrefslogtreecommitdiff
path: root/jstests/core/write/crud_api.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/write/crud_api.js')
-rw-r--r--jstests/core/write/crud_api.js730
1 files changed, 730 insertions, 0 deletions
diff --git a/jstests/core/write/crud_api.js b/jstests/core/write/crud_api.js
new file mode 100644
index 00000000000..de8d72e50af
--- /dev/null
+++ b/jstests/core/write/crud_api.js
@@ -0,0 +1,730 @@
+// Cannot implicitly shard accessed collections because of following errmsg: A single
+// update/delete on a sharded collection must contain an exact match on _id or contain the shard
+// key.
+//
+// @tags: [
+// assumes_unsharded_collection,
+// assumes_write_concern_unchanged,
+// requires_fastcount,
+// # This test writes with {w: 0} which doesn't wait for the storage transaction writing the
+// # document and the oplog entry to commit so the TenantMigrationConflict will not be caught.
+// tenant_migration_incompatible,
+// ]
+
+(function() {
+"use strict";
+
+load("jstests/aggregation/extras/utils.js"); // For arrayEq.
+
+var crudAPISpecTests = function crudAPISpecTests() {
+ "use strict";
+
+ // Get the colllection
+ var coll = db.crud_tests;
+
+ // Setup
+ function createTestExecutor(coll, method, verifyResult) {
+ return function(args) {
+ // Drop collection
+ coll.drop();
+ // Insert test data
+ var r = coll.insertMany(args.insert);
+ assert.eq(args.insert.length, r.insertedIds.length);
+
+ // Execute the method with arguments
+ r = coll[method].apply(coll, args.params);
+ verifyResult(args.result, r);
+
+ // Get all the results
+ assert.soonNoExcept(
+ function() {
+ var results = coll.find({}).sort({_id: 1}).toArray();
+ assert.docEq(args.expected, results);
+ return true;
+ },
+ function() {
+ return "collection never contained expected documents";
+ });
+ };
+ }
+
+ function checkResultObject(expected, actual) {
+ // Only assert on the "modifiedCount" property
+ assert.docEq(expected, actual);
+ }
+
+ // Setup executors
+ var deleteManyExecutor = createTestExecutor(coll, 'deleteMany', checkResultObject);
+ var deleteOneExecutor = createTestExecutor(coll, 'deleteOne', checkResultObject);
+ var bulkWriteExecutor = createTestExecutor(coll, 'bulkWrite', checkResultObject);
+ var findOneAndDeleteExecutor = createTestExecutor(coll, 'findOneAndDelete', checkResultObject);
+ var findOneAndReplaceExecutor =
+ createTestExecutor(coll, 'findOneAndReplace', checkResultObject);
+ var findOneAndUpdateExecutor = createTestExecutor(coll, 'findOneAndUpdate', checkResultObject);
+ var insertManyExecutor = createTestExecutor(coll, 'insertMany', checkResultObject);
+ var insertOneExecutor = createTestExecutor(coll, 'insertOne', checkResultObject);
+ var replaceOneExecutor = createTestExecutor(coll, 'replaceOne', checkResultObject);
+ var updateManyExecutor = createTestExecutor(coll, 'updateMany', checkResultObject);
+ var updateOneExecutor = createTestExecutor(coll, 'updateOne', checkResultObject);
+ var countExecutor = createTestExecutor(coll, 'count', assert.eq);
+ var distinctExecutor = createTestExecutor(coll, 'distinct', (a, b) => assert(arrayEq(a, b)));
+
+ //
+ // BulkWrite
+ //
+
+ bulkWriteExecutor({
+ insert: [{_id: 1, c: 1}, {_id: 2, c: 2}, {_id: 3, c: 3}],
+ params: [[
+ {insertOne: {document: {_id: 4, a: 1}}},
+ {updateOne: {filter: {_id: 5, a: 2}, update: {$set: {a: 2}}, upsert: true}},
+ {updateMany: {filter: {_id: 6, a: 3}, update: {$set: {a: 3}}, upsert: true}},
+ {deleteOne: {filter: {c: 1}}},
+ {insertOne: {document: {_id: 7, c: 2}}},
+ {deleteMany: {filter: {c: 2}}},
+ {replaceOne: {filter: {c: 3}, replacement: {c: 4}, upsert: true}}
+ ]],
+ result: {
+ acknowledged: true,
+ insertedCount: 2,
+ matchedCount: 1,
+ deletedCount: 3,
+ upsertedCount: 2,
+ insertedIds: {'0': 4, '4': 7},
+ upsertedIds: {'1': 5, '2': 6}
+ },
+ expected: [{"_id": 3, "c": 4}, {"_id": 4, "a": 1}, {"_id": 5, "a": 2}, {"_id": 6, "a": 3}]
+ });
+
+ bulkWriteExecutor({
+ insert: [{_id: 1, c: 1}, {_id: 2, c: 2}, {_id: 3, c: 3}],
+ params: [
+ [
+ {insertOne: {document: {_id: 4, a: 1}}},
+ {updateOne: {filter: {_id: 5, a: 2}, update: {$set: {a: 2}}, upsert: true}},
+ {updateMany: {filter: {_id: 6, a: 3}, update: {$set: {a: 3}}, upsert: true}},
+ {deleteOne: {filter: {c: 1}}},
+ {deleteMany: {filter: {c: 2}}},
+ {replaceOne: {filter: {c: 3}, replacement: {c: 4}, upsert: true}}
+ ],
+ {ordered: false}
+ ],
+ result: {
+ acknowledged: true,
+ insertedCount: 1,
+ matchedCount: 1,
+ deletedCount: 2,
+ upsertedCount: 2,
+ insertedIds: {'0': 4},
+ upsertedIds: {'1': 5, '2': 6}
+ },
+ expected: [{"_id": 3, "c": 4}, {"_id": 4, "a": 1}, {"_id": 5, "a": 2}, {"_id": 6, "a": 3}]
+ });
+
+ // DeleteMany
+ //
+
+ // DeleteMany when many documents match
+ deleteManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}],
+ result: {acknowledged: true, deletedCount: 2},
+ expected: [{_id: 1, x: 11}]
+ });
+ // DeleteMany when no document matches
+ deleteManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}],
+ result: {acknowledged: true, deletedCount: 0},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // DeleteMany when many documents match, no write concern
+ deleteManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}]
+ });
+
+ //
+ // DeleteOne
+ //
+
+ // DeleteOne when many documents match
+ deleteOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}],
+ result: {acknowledged: true, deletedCount: 1},
+ expected: [{_id: 1, x: 11}, {_id: 3, x: 33}]
+ });
+ // DeleteOne when one document matches
+ deleteOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 2}],
+ result: {acknowledged: true, deletedCount: 1},
+ expected: [{_id: 1, x: 11}, {_id: 3, x: 33}]
+ });
+ // DeleteOne when no documents match
+ deleteOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}],
+ result: {acknowledged: true, deletedCount: 0},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // DeleteOne when many documents match, no write concern
+ deleteOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 3, x: 33}]
+ });
+
+ //
+ // FindOneAndDelete
+ //
+
+ // FindOneAndDelete when one document matches
+ findOneAndDeleteExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 2}}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 33},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}]
+ });
+ // FindOneAndDelete when one document matches
+ findOneAndDeleteExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 2}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 22},
+ expected: [{_id: 1, x: 11}, {_id: 3, x: 33}]
+ });
+ // FindOneAndDelete when no documents match
+ findOneAndDeleteExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+
+ //
+ // FindOneAndReplace
+ //
+
+ // FindOneAndReplace when many documents match returning the document before modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {x: 32}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 22},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 32}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when many documents match returning the document after modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: {$gt: 1}},
+ {x: 32},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: {x: 32},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 32}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when one document matches returning the document before modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 2}, {x: 32}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 22},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 32}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when one document matches returning the document after modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 2},
+ {x: 32},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: {x: 32},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 32}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when no documents match returning the document before modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {x: 44}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when no documents match with upsert returning the document before
+ // modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {x: 44}, {projection: {x: 1, _id: 0}, sort: {x: 1}, upsert: true}],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 44}]
+ });
+ // FindOneAndReplace when no documents match returning the document after modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 4},
+ {x: 44},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // FindOneAndReplace when no documents match with upsert returning the document after
+ // modification
+ findOneAndReplaceExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 4},
+ {x: 44},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true, upsert: true}
+ ],
+ result: {x: 44},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 44}]
+ });
+
+ assert.throws(function() {
+ coll.findOneAndReplace({a: 1}, {$set: {b: 1}});
+ });
+
+ //
+ // FindOneAndUpdate
+ //
+
+ // FindOneAndUpdate when many documents match returning the document before modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {$inc: {x: 1}}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 22},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when many documents match returning the document after modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: {$gt: 1}},
+ {$inc: {x: 1}},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: {x: 23},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when one document matches returning the document before modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 2}, {$inc: {x: 1}}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: {x: 22},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when one document matches returning the document after modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 2},
+ {$inc: {x: 1}},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: {x: 23},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when no documents match returning the document before modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}, {projection: {x: 1, _id: 0}, sort: {x: 1}}],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when no documents match with upsert returning the document before
+ // modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params:
+ [{_id: 4}, {$inc: {x: 1}}, {projection: {x: 1, _id: 0}, sort: {x: 1}, upsert: true}],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // FindOneAndUpdate when no documents match returning the document after modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 4},
+ {$inc: {x: 1}},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true}
+ ],
+ result: null,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // FindOneAndUpdate when no documents match with upsert returning the document after
+ // modification
+ findOneAndUpdateExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [
+ {_id: 4},
+ {$inc: {x: 1}},
+ {projection: {x: 1, _id: 0}, sort: {x: 1}, returnNewDocument: true, upsert: true}
+ ],
+ result: {x: 1},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+
+ assert.throws(function() {
+ coll.findOneAndUpdate({a: 1}, {});
+ });
+
+ assert.throws(function() {
+ coll.findOneAndUpdate({a: 1}, {b: 1});
+ });
+
+ //
+ // InsertMany
+ //
+
+ // InsertMany with non-existing documents
+ insertManyExecutor({
+ insert: [{_id: 1, x: 11}],
+ params: [[{_id: 2, x: 22}, {_id: 3, x: 33}]],
+ result: {acknowledged: true, insertedIds: [2, 3]},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // InsertMany with non-existing documents, no write concern
+ insertManyExecutor({
+ insert: [{_id: 1, x: 11}],
+ params: [[{_id: 2, x: 22}, {_id: 3, x: 33}], {w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+
+ //
+ // InsertOne
+ //
+
+ // InsertOne with non-existing documents
+ insertOneExecutor({
+ insert: [{_id: 1, x: 11}],
+ params: [{_id: 2, x: 22}],
+ result: {acknowledged: true, insertedId: 2},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}]
+ });
+ // InsertOne with non-existing documents, no write concern
+ insertOneExecutor({
+ insert: [{_id: 1, x: 11}],
+ params: [{_id: 2, x: 22}, {w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}]
+ });
+
+ //
+ // ReplaceOne
+ //
+
+ // ReplaceOne when many documents match
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {x: 111}],
+ result: {acknowledged: true, matchedCount: 1, modifiedCount: 1},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 111}, {_id: 3, x: 33}]
+ });
+ // ReplaceOne when one document matches
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 1}, {_id: 1, x: 111}],
+ result: {acknowledged: true, matchedCount: 1, modifiedCount: 1},
+ expected: [{_id: 1, x: 111}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // ReplaceOne when no documents match
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {_id: 4, x: 1}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // ReplaceOne with upsert when no documents match without an id specified
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {x: 1}, {upsert: true}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0, upsertedId: 4},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // ReplaceOne with upsert when no documents match with an id specified
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {_id: 4, x: 1}, {upsert: true}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0, upsertedId: 4},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // ReplaceOne with upsert when no documents match with an id specified, no write concern
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {_id: 4, x: 1}, {upsert: true, w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // ReplaceOne with upsert when no documents match with an id specified, no write concern
+ replaceOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {_id: 4, x: 1}, {upsert: true, writeConcern: {w: 0}}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+
+ assert.throws(function() {
+ coll.replaceOne({a: 1}, {$set: {b: 1}});
+ });
+
+ //
+ // UpdateMany
+ //
+
+ // UpdateMany when many documents match
+ updateManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 2, modifiedCount: 2},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 34}]
+ });
+ // UpdateMany when one document matches
+ updateManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 1}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 1, modifiedCount: 1},
+ expected: [{_id: 1, x: 12}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // UpdateMany when no documents match
+ updateManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // UpdateMany with upsert when no documents match
+ updateManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}, {upsert: true}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0, upsertedId: 4},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // UpdateMany with upsert when no documents match, no write concern
+ updateManyExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}, {upsert: true, w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+
+ assert.throws(function() {
+ coll.updateMany({a: 1}, {});
+ });
+
+ assert.throws(function() {
+ coll.updateMany({a: 1}, {b: 1});
+ });
+
+ //
+ // UpdateOne
+ //
+
+ // UpdateOne when many documents match
+ updateOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 1, modifiedCount: 1},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+ // UpdateOne when one document matches
+ updateOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 1}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 1, modifiedCount: 1},
+ expected: [{_id: 1, x: 12}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // UpdateOne when no documents match
+ updateOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+
+ // UpdateOne with upsert when no documents match
+ updateOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: 4}, {$inc: {x: 1}}, {upsert: true}],
+ result: {acknowledged: true, matchedCount: 0, modifiedCount: 0, upsertedId: 4},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}, {_id: 4, x: 1}]
+ });
+ // UpdateOne when many documents match, no write concern
+ updateOneExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}, {$inc: {x: 1}}, {w: 0}],
+ result: {acknowledged: false},
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 23}, {_id: 3, x: 33}]
+ });
+
+ assert.throws(function() {
+ coll.updateOne({a: 1}, {});
+ });
+
+ assert.throws(function() {
+ coll.updateOne({a: 1}, {b: 1});
+ });
+
+ //
+ // Count
+ //
+
+ // Simple count of all elements
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{}],
+ result: 3,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple count no arguments
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [],
+ result: 3,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple count filtered
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{_id: {$gt: 1}}],
+ result: 2,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple count of all elements, applying limit
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{}, {limit: 1}],
+ result: 1,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple count of all elements, applying skip
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{}, {skip: 1}],
+ result: 2,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple count no arguments, applying hint
+ countExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: [{}, {hint: {"_id": 1}}],
+ result: 3,
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+
+ //
+ // Distinct
+ //
+
+ // Simple distinct of field x no filter
+ distinctExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: ['x'],
+ result: [11, 22, 33],
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple distinct of field x
+ distinctExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: ['x', {}],
+ result: [11, 22, 33],
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple distinct of field x filtered
+ distinctExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: ['x', {x: {$gt: 11}}],
+ result: [22, 33],
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+ // Simple distinct of field x filtered with maxTimeMS
+ distinctExecutor({
+ insert: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}],
+ params: ['x', {x: {$gt: 11}}, {maxTimeMS: 100000}],
+ result: [22, 33],
+ expected: [{_id: 1, x: 11}, {_id: 2, x: 22}, {_id: 3, x: 33}]
+ });
+
+ //
+ // Find
+ //
+
+ coll.deleteMany({});
+ // Insert all of them
+ coll.insertMany([{a: 0, b: 0}, {a: 1, b: 1}]);
+
+ // Simple projection
+ var result = coll.find({}).sort({a: 1}).limit(1).skip(1).projection({_id: 0, a: 1}).toArray();
+ assert.docEq([{a: 1}], result);
+
+ // Simple tailable cursor
+ var cursor = coll.find({}).sort({a: 1}).tailable();
+ assert.eq(34, (cursor._options & ~DBQuery.Option.slaveOk));
+ var cursor = coll.find({}).sort({a: 1}).tailable(false);
+ assert.eq(2, (cursor._options & ~DBQuery.Option.slaveOk));
+
+ // allowPartialResults
+ var cursor = coll.find({}).allowPartialResults();
+ assert.eq(128, (cursor._options & ~DBQuery.Option.slaveOk));
+
+ // noCursorTimeout
+ var cursor = coll.find({}).noCursorTimeout();
+ assert.eq(16, (cursor._options & ~DBQuery.Option.slaveOk));
+
+ //
+ // Aggregation
+ //
+
+ coll.deleteMany({});
+ // Insert all of them
+ coll.insertMany([{a: 0, b: 0}, {a: 1, b: 1}]);
+
+ // Simple aggregation with useCursor
+ var result = coll.aggregate([{$match: {}}], {useCursor: true}).toArray();
+ assert.eq(2, result.length);
+
+ // Simple aggregation with batchSize
+ var result = coll.aggregate([{$match: {}}], {batchSize: 2}).toArray();
+ assert.eq(2, result.length);
+
+ // Drop collection
+ coll.drop();
+ coll.createIndex({a: 1}, {unique: true});
+
+ // Should throw duplicate key error
+ assert.throws(function() {
+ coll.insertMany([{a: 0, b: 0}, {a: 0, b: 1}]);
+ });
+
+ assert(coll.findOne({a: 0, b: 0}) != null);
+ assert.throws(function() {
+ coll.insertOne({a: 0, b: 0});
+ });
+
+ assert.throws(function() {
+ coll.updateOne({b: 2}, {$set: {a: 0}}, {upsert: true});
+ });
+
+ assert.throws(function() {
+ coll.updateMany({b: 2}, {$set: {a: 0}}, {upsert: true});
+ });
+
+ assert.throws(function() {
+ coll.deleteOne({$invalidFieldName: {a: 1}});
+ });
+
+ assert.throws(function() {
+ coll.deleteMany({$set: {a: 1}});
+ });
+
+ assert.throws(function() {
+ coll.bulkWrite([{insertOne: {document: {_id: 4, a: 0}}}]);
+ });
+};
+
+crudAPISpecTests();
+})();