summaryrefslogtreecommitdiff
path: root/jstests/core/verify_update_mods.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/verify_update_mods.js')
-rw-r--r--jstests/core/verify_update_mods.js374
1 files changed, 301 insertions, 73 deletions
diff --git a/jstests/core/verify_update_mods.js b/jstests/core/verify_update_mods.js
index 134668e62bd..184b98ef14d 100644
--- a/jstests/core/verify_update_mods.js
+++ b/jstests/core/verify_update_mods.js
@@ -1,82 +1,310 @@
-// 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, requires_non_retryable_writes]
+/**
+ * Tests update and findAndModify command behavior with update modifiers.
+ *
+ * @tags: [
+ * requires_fcv_42,
+ * # The test is designed to work with an unsharded collection.
+ * assumes_unsharded_collection,
+ * # The coll.update command does not work with $set operator in compatibility write mode.
+ * requires_find_command,
+ * # Performs modifications that if repeated would fail the test.
+ * requires_non_retryable_writes,
+ * ]
+ */
+(function() {
+"use strict";
-// Verify update mods exist
-var res;
-t = db.update_mods;
-t.drop();
+const testDB = db.getSiblingDB(jsTestName());
+assert.commandWorked(testDB.dropDatabase());
+const coll = testDB.update_modifiers;
-t.save({_id: 1});
-res = t.update({}, {$set: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+// Executes a test case which inserts documents into the test collection, issues an update command,
+// and verifies that the results are as expected.
+function executeUpdateTestCase(testCase) {
+ jsTestLog(tojson(testCase));
-t.save({_id: 1});
-res = t.update({}, {$unset: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+ // Remove all existing documents and then insert the new test case's documents.
+ assert.commandWorked(coll.remove({}));
+ assert.commandWorked(coll.insert(testCase.inputDocuments));
-t.save({_id: 1});
-res = t.update({}, {$inc: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+ // Issue the update command specified in the test case.
+ const result = testCase.command(coll, testCase.query, testCase.update, testCase.arrayFilters);
-t.save({_id: 1});
-res = t.update({}, {$mul: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+ if (testCase.expectedErrorCode == undefined) {
+ // Verify that the command succeeded and collection's contents match the expected results.
+ assert.commandWorked(result);
+ assert.docEq(coll.find({}).sort({_id: 1}).toArray(), testCase.expectedResults);
+ } else {
+ assert.commandFailedWithCode(result, testCase.expectedErrorCode);
+ }
+}
-t.save({_id: 1});
-res = t.update({}, {$push: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+// Issues the update command and returns the response.
+function updateCommand(coll, query, update, arrayFilters) {
+ const commandOptions = {upsert: true};
+ if (arrayFilters !== undefined) {
+ commandOptions.arrayFilters = arrayFilters;
+ }
+ return coll.update(query, update, commandOptions);
+}
-res = t.update({}, {$addToSet: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+// Issues the findAndModify command and returns the response.
+function findAndModifyCommand(coll, query, update, arrayFilters) {
+ const command = {query: query, update: update, upsert: true};
+ if (arrayFilters !== undefined) {
+ command.arrayFilters = arrayFilters;
+ }
+ return coll.runCommand("findAndModify", command);
+}
-t.save({_id: 1});
-res = t.update({}, {$pull: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+// Tests all relevant update modifiers with set and empty update documents.
+const testCases = [
+ {
+ query: {_id: 1},
+ update: {$set: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 2},
+ update: {$set: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}, {_id: 2}],
+ },
+ {
+ query: {_id: 1},
+ update: {$set: {a: 2}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 2}],
+ },
+ {
+ query: {_id: 1},
+ update: {$set: {}},
+ arrayFilters: [{"element": {$gt: 6}}],
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedErrorCode: ErrorCodes.FailedToParse,
+ },
+ {
+ query: {_id: 1},
+ update: {$unset: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$unset: {a: 1}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$inc: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$inc: {a: 1}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 2}],
+ },
+ {
+ query: {_id: 1},
+ update: {$mul: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$mul: {a: 2}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 2}],
+ },
+ {
+ query: {_id: 1},
+ update: {$push: {}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$push: {a: 2}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1, 2]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$addToSet: {}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$addToSet: {a: 2}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1, 2]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pull: {}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pull: {a: 1}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: []}],
+ },
+ {
+ query: {_id: 1},
+ update: {$rename: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$rename: {a: "b"}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, b: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$bit: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$bit: {a: {and: NumberLong(1)}}},
+ inputDocuments: [{_id: 1, a: NumberLong(2)}],
+ expectedResults: [{_id: 1, a: NumberLong(0)}],
+ },
+ {
+ query: {_id: 1},
+ update: {$bit: {a: {and: NumberLong(3)}}},
+ inputDocuments: [],
+ expectedResults: [{_id: 1, a: NumberLong(0)}],
+ },
+ {
+ query: {_id: 1},
+ update: {$bit: {b: {or: NumberLong(3)}}},
+ inputDocuments: [],
+ expectedResults: [{_id: 1, b: NumberLong(3)}],
+ },
+ {
+ query: {_id: 1},
+ update: {$bit: {"c.d": {or: NumberInt(3)}}},
+ inputDocuments: [],
+ expectedResults: [{_id: 1, c: {d: NumberInt(3)}}],
+ },
+ {
+ query: {_id: 1},
+ update: {$max: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$max: {a: 2}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 2}],
+ },
+ {
+ query: {_id: 1},
+ update: {$min: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$min: {a: 0}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 0}],
+ },
+ {
+ query: {_id: 1},
+ update: {$currentDate: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$setOnInsert: {}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 2},
+ update: {$setOnInsert: {a: 1}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}, {_id: 2, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$setOnInsert: {a: 2}},
+ inputDocuments: [{_id: 1, a: 1}],
+ expectedResults: [{_id: 1, a: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {}},
+ inputDocuments: [{_id: 1, a: [1]}],
+ expectedResults: [{_id: 1, a: [1]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {a: true}},
+ inputDocuments: [{_id: 1, a: [1, 2]}],
+ expectedErrorCode: ErrorCodes.FailedToParse,
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {a: 1}},
+ inputDocuments: [{_id: 1, a: [1, 2]}],
+ expectedResults: [{_id: 1, a: [1]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {a: -1}},
+ inputDocuments: [{_id: 1, a: [1, 2]}],
+ expectedResults: [{_id: 1, a: [2]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {a: 1}},
+ inputDocuments: [{_id: 1, a: []}],
+ expectedResults: [{_id: 1, a: []}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pop: {a: 1}},
+ inputDocuments: [],
+ expectedResults: [{_id: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$currentDate: {}},
+ inputDocuments: [{_id: 1}],
+ expectedResults: [{_id: 1}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pullAll: {}},
+ inputDocuments: [{_id: 1, a: [1, 2]}],
+ expectedResults: [{_id: 1, a: [1, 2]}],
+ },
+ {
+ query: {_id: 1},
+ update: {$pullAll: {a: [1]}},
+ inputDocuments: [{_id: 1, a: [1, 2, 1]}],
+ expectedResults: [{_id: 1, a: [2]}],
+ },
+];
-t.save({_id: 1});
-res = t.update({}, {$pop: {a: true}});
-assert.writeError(res);
-t.remove({});
-
-t.save({_id: 1});
-res = t.update({}, {$rename: {a: "b"}});
-assert.writeOK(res);
-t.remove({});
-
-t.save({_id: 1});
-res = t.update({}, {$bit: {a: {and: NumberLong(1)}}});
-assert.writeOK(res);
-t.remove({});
-
-// SERVER-3223 test $bit can do an upsert
-t.update({_id: 1}, {$bit: {a: {and: NumberLong(3)}}}, true);
-assert.eq(t.findOne({_id: 1}).a, NumberLong(0), "$bit upsert with and");
-t.update({_id: 2}, {$bit: {b: {or: NumberLong(3)}}}, true);
-assert.eq(t.findOne({_id: 2}).b, NumberLong(3), "$bit upsert with or (long)");
-t.update({_id: 3}, {$bit: {"c.d": {or: NumberInt(3)}}}, true);
-assert.eq(t.findOne({_id: 3}).c.d, NumberInt(3), "$bit upsert with or (int)");
-t.remove({});
-
-t.save({_id: 1});
-res = t.update({}, {$currentDate: {a: true}});
-assert.writeOK(res);
-t.remove({});
-
-t.save({_id: 1});
-res = t.update({}, {$max: {a: 1}});
-assert.writeOK(res);
-t.remove({});
-
-t.save({_id: 1});
-res = t.update({}, {$min: {a: 1}});
-assert.writeOK(res);
-t.remove({});
+for (const command of [updateCommand, findAndModifyCommand]) {
+ for (const testCase of testCases) {
+ executeUpdateTestCase(Object.assign({command: command}, testCase));
+ }
+}
+})();