mongo/jstests/libs/write_concern_all_commands.js

6463 lines
295 KiB
JavaScript

/**
* Tests that commands that accept write concern correctly return write concern errors.
*
* Every command that accepts writeConcern should define a test case. Test cases should consist of a
* no-op case, success case, and failure case where applicable. The no-op and failure scenarios
* should mimic scenarios where it's necessary to wait for write concern because the outcome of the
* request indicates to the user that the data is in some definitive state, e.g. the write is
* identical to a previous write, or the user has set up schema validation rules that would cause
* the current write to fail.
*
*/
import {TimeseriesTest} from "jstests/core/timeseries/libs/timeseries.js";
import {AllCommandsTest} from "jstests/libs/all_commands_test.js";
import {getCommandName} from "jstests/libs/cmd_object_utils.js";
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";
import {Thread} from "jstests/libs/parallelTester.js";
import {assertWriteConcernError} from "jstests/libs/write_concern_util.js";
const dbName = "testDB";
const collName = "testColl";
const fullNs = dbName + "." + collName;
const timeValue = ISODate("2015-12-31T23:59:59.000Z");
// Set up txn state to be used in the transaction test cases
let _lsid = UUID();
let _txnNum = 0;
function getLSID() {
return {id: _lsid};
}
function getTxnNumber() {
return NumberLong(_txnNum);
}
function genNextTxnNumber() {
_txnNum++;
}
function getShardNames(cluster) {
return [cluster.shard0.shardName, cluster.shard1.shardName];
}
function getShardKeyMinRanges(coll) {
let a = coll.getDB().getSiblingDB("config").chunks.find({uuid: coll.getUUID()}, {min: 1, _id: 0}).sort({min: 1});
return a.toArray();
}
// TODO SERVER-97754 Remove these 2 functions, and do not stop the remaining secondaries once these
// commands no longer override user provided writeConcern
let stopAdditionalSecondariesIfSharded = function (clusterType, cluster, secondariesRunning) {
if (clusterType == "sharded") {
const shards = cluster.getAllShards();
for (let i = 0; i < shards.length; i++) {
secondariesRunning[i].getDB("admin").fsyncLock();
}
}
};
let restartAdditionalSecondariesIfSharded = function (clusterType, cluster, secondariesRunning) {
if (clusterType == "sharded") {
const shards = cluster.getAllShards();
for (let i = 0; i < shards.length; i++) {
secondariesRunning[i].getDB("admin").fsyncUnlock();
}
}
};
let getShardKey = (coll, fullNs) => {
let entry = coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}, {key: 1});
// If there is no shard key, return an empty obj
if (!entry) {
return {};
}
return entry.key;
};
// All commands in the server.
const wcCommandsTests = {
_addShard: {skip: "internal command"},
_cloneCollectionOptionsFromPrimaryShard: {skip: "internal command"},
_clusterQueryWithoutShardKey: {skip: "internal command"},
_clusterWriteWithoutShardKey: {skip: "internal command"},
_configsvrAbortReshardCollection: {skip: "internal command"},
_configsvrAddShard: {skip: "internal command"},
_configsvrAddShardToZone: {skip: "internal command"},
_configsvrBalancerCollectionStatus: {skip: "internal command"},
_configsvrBalancerStart: {skip: "internal command"},
_configsvrBalancerStatus: {skip: "internal command"},
_configsvrBalancerStop: {skip: "internal command"},
_configsvrCheckClusterMetadataConsistency: {skip: "internal command"},
_configsvrCheckMetadataConsistency: {skip: "internal command"},
_configsvrCleanupReshardCollection: {skip: "internal command"},
_configsvrCollMod: {skip: "internal command"},
_configsvrClearJumboFlag: {skip: "internal command"},
_configsvrCommitChunksMerge: {skip: "internal command"},
_configsvrCommitChunkMigration: {skip: "internal command"},
_configsvrCommitChunkSplit: {skip: "internal command"},
_configsvrCommitMergeAllChunksOnShard: {skip: "internal command"},
_configsvrCommitMovePrimary: {skip: "internal command"},
_configsvrCommitRefineCollectionShardKey: {skip: "internal command"},
_configsvrCommitReshardCollection: {skip: "internal command"},
_configsvrCommitShardRemoval: {skip: "internal command"},
_configsvrConfigureCollectionBalancing: {skip: "internal command"},
_configsvrCreateDatabase: {skip: "internal command"},
_configsvrEnsureChunkVersionIsGreaterThan: {skip: "internal command"},
_configsvrGetHistoricalPlacement: {skip: "internal command"},
_configsvrMoveRange: {skip: "internal command"},
_configsvrRemoveChunks: {skip: "internal command"},
_configsvrRemoveShard: {skip: "internal command"},
_configsvrRemoveShardFromZone: {skip: "internal command"},
_configsvrRemoveTags: {skip: "internal command"},
_configsvrRenameCollection: {skip: "internal command"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
_configsvrResetPlacementHistory: {skip: "internal command"},
_configsvrReshardCollection: {skip: "internal command"},
_configsvrRunRestore: {skip: "internal command"},
_configsvrSetAllowMigrations: {skip: "internal command"},
_configsvrSetClusterParameter: {skip: "internal command"},
_configsvrSetUserWriteBlockMode: {skip: "internal command"},
_configsvrShardDrainingStatus: {skip: "internal command"},
_configsvrStartShardDraining: {skip: "internal command"},
_configsvrStopShardDraining: {skip: "internal command"},
_configsvrTransitionFromDedicatedConfigServer: {skip: "internal command"},
_configsvrTransitionToDedicatedConfigServer: {skip: "internal command"},
_configsvrUpdateZoneKeyRange: {skip: "internal command"},
_dropConnectionsToMongot: {skip: "internal command"},
_dropMirrorMaestroConnections: {skip: "internal command"},
_flushDatabaseCacheUpdates: {skip: "internal command"},
_flushDatabaseCacheUpdatesWithWriteConcern: {skip: "internal command"},
_flushReshardingStateChange: {skip: "internal command"},
_flushRoutingTableCacheUpdates: {skip: "internal command"},
_flushRoutingTableCacheUpdatesWithWriteConcern: {skip: "internal command"},
_getNextSessionMods: {skip: "internal command"},
_getUserCacheGeneration: {skip: "internal command"},
_hashBSONElement: {skip: "internal command"},
_isSelf: {skip: "internal command"},
_killOperations: {skip: "internal command"},
_mergeAuthzCollections: {skip: "internal command"},
_migrateClone: {skip: "internal command"},
_mirrorMaestroConnPoolStats: {skip: "internal command"},
_mongotConnPoolStats: {skip: "internal command"},
_recvChunkAbort: {skip: "internal command"},
_recvChunkCommit: {skip: "internal command"},
_recvChunkReleaseCritSec: {skip: "internal command"},
_recvChunkStart: {skip: "internal command"},
_recvChunkStatus: {skip: "internal command"},
_refreshQueryAnalyzerConfiguration: {skip: "internal command"},
_shardsvrAbortReshardCollection: {skip: "internal command"},
_shardsvrBeginMigrationBlockingOperation: {skip: "internal command"},
_shardsvrChangePrimary: {skip: "internal command"},
_shardsvrCleanupReshardCollection: {skip: "internal command"},
_shardsvrCloneAuthoritativeMetadata: {skip: "internal command"},
_shardsvrCloneCatalogData: {skip: "internal command"},
_shardsvrCommitCreateDatabaseMetadata: {skip: "internal command"},
_shardsvrCommitDropDatabaseMetadata: {skip: "internal command"},
_shardsvrCheckMetadataConsistency: {skip: "internal command"},
_shardsvrCheckMetadataConsistencyParticipant: {skip: "internal command"},
_shardsvrCleanupStructuredEncryptionData: {skip: "internal command"},
_shardsvrCommitReshardCollection: {skip: "internal command"},
_shardsvrCompactStructuredEncryptionData: {skip: "internal command"},
_shardsvrConvertToCapped: {skip: "internal command"},
_shardsvrCoordinateMultiUpdate: {skip: "internal command"},
_shardsvrCreateCollection: {skip: "internal command"},
_shardsvrCreateCollectionParticipant: {skip: "internal command"},
_shardsvrDrainOngoingDDLOperations: {skip: "internal command"},
_shardsvrDropCollection: {skip: "internal command"},
_shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: {skip: "internal command"},
_shardsvrDropCollectionParticipant: {skip: "internal command"},
_shardsvrDropIndexes: {skip: "internal command"},
_shardsvrDropIndexesParticipant: {skip: "internal command"},
_shardsvrDropDatabase: {skip: "internal command"},
_shardsvrDropDatabaseParticipant: {skip: "internal command"},
_shardsvrEndMigrationBlockingOperation: {skip: "internal command"},
_shardsvrGetStatsForBalancing: {skip: "internal command"},
_shardsvrCheckCanConnectToConfigServer: {skip: "internal command"},
_shardsvrJoinDDLCoordinators: {skip: "internal command"},
_shardsvrJoinMigrations: {skip: "internal command"},
_shardsvrMergeAllChunksOnShard: {skip: "internal command"},
_shardsvrMergeChunks: {skip: "internal command"},
_shardsvrMovePrimary: {skip: "internal command"},
_shardsvrMovePrimaryEnterCriticalSection: {skip: "internal command"},
_shardsvrMovePrimaryExitCriticalSection: {skip: "internal command"},
_shardsvrMoveRange: {skip: "internal command"},
_shardsvrNotifyShardingEvent: {skip: "internal command"},
_shardsvrRefineCollectionShardKey: {skip: "internal command"},
_shardsvrRenameCollection: {skip: "internal command"},
_shardsvrRenameCollectionParticipant: {skip: "internal command"},
_shardsvrRenameCollectionParticipantUnblock: {skip: "internal command"},
_shardsvrRenameIndexMetadata: {skip: "internal command"},
_shardsvrReshardCollection: {skip: "internal command"},
_shardsvrReshardingDonorFetchFinalCollectionStats: {skip: "internal command"},
_shardsvrReshardingDonorStartChangeStreamsMonitor: {skip: "internal command"},
_shardsvrReshardingOperationTime: {skip: "internal command"},
_shardsvrReshardRecipientClone: {skip: "internal command"},
_shardsvrResolveView: {skip: "internal command"},
_shardsvrRunSearchIndexCommand: {skip: "internal command"},
_shardsvrSetAllowMigrations: {skip: "internal command"},
_shardsvrSetClusterParameter: {skip: "internal command"},
_shardsvrSetUserWriteBlockMode: {skip: "internal command"},
_shardsvrValidateShardKeyCandidate: {skip: "internal command"},
_shardsvrCollMod: {skip: "internal command"},
_shardsvrCollModParticipant: {skip: "internal command"},
_shardsvrConvertToCappedParticipant: {skip: "internal command"},
_shardsvrParticipantBlock: {skip: "internal command"},
_shardsvrUntrackUnsplittableCollection: {skip: "internal command"},
_shardsvrFetchCollMetadata: {skip: "internal command"},
streams_startStreamProcessor: {skip: "internal command"},
streams_startStreamSample: {skip: "internal command"},
streams_stopStreamProcessor: {skip: "internal command"},
streams_listStreamProcessors: {skip: "internal command"},
streams_getMoreStreamSample: {skip: "internal command"},
streams_getStats: {skip: "internal command"},
streams_testOnlyInsert: {skip: "internal command"},
streams_getMetrics: {skip: "internal command"},
streams_updateFeatureFlags: {skip: "internal command"},
streams_testOnlyGetFeatureFlags: {skip: "internal command"},
streams_writeCheckpoint: {skip: "internal command"},
streams_sendEvent: {skip: "internal command"},
streams_updateConnection: {skip: "internal command"},
_transferMods: {skip: "internal command"},
abortMoveCollection: {skip: "does not accept write concern"},
abortReshardCollection: {skip: "does not accept write concern"},
abortRewriteCollection: {skip: "does not accept write concern"},
abortTransaction: {
success: {
// Basic abort transaction
req: () => ({
abortTransaction: 1,
txnNumber: getTxnNumber(),
autocommit: false,
lsid: getLSID(),
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 0}));
if (clusterType == "sharded" && bsonWoCompare(getShardKey(coll, fullNs), {}) == 0) {
// Set the primary shard to shard0 so we can assume that it's okay to run
// prepareTransaction on it
assert.commandWorked(
coll.getDB().adminCommand({moveCollection: fullNs, toShard: cluster.shard0.shardName}),
);
}
assert.commandWorked(
coll.getDB().runCommand({
insert: collName,
documents: [{_id: 1}],
lsid: getLSID(),
stmtIds: [NumberInt(0)],
txnNumber: getTxnNumber(),
startTransaction: true,
autocommit: false,
}),
);
assert.commandWorked(
coll.getDB().runCommand({
update: collName,
updates: [{q: {}, u: {$set: {a: 1}}}],
lsid: getLSID(),
stmtIds: [NumberInt(1)],
txnNumber: getTxnNumber(),
autocommit: false,
}),
);
let primary = clusterType == "sharded" ? cluster.rs0.getPrimary() : cluster.getPrimary();
assert.commandWorked(
primary.getDB(dbName).adminCommand({
prepareTransaction: 1,
lsid: getLSID(),
txnNumber: getTxnNumber(),
autocommit: false,
}),
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
genNextTxnNumber();
},
admin: true,
},
failure: {
// The transaction has already been committed
req: () => ({
abortTransaction: 1,
txnNumber: getTxnNumber(),
autocommit: false,
lsid: getLSID(),
}),
setupFunc: (coll) => {
assert.commandWorked(
coll.getDB().runCommand({
insert: collName,
documents: [{_id: 1}],
lsid: getLSID(),
stmtIds: [NumberInt(0)],
txnNumber: getTxnNumber(),
startTransaction: true,
autocommit: false,
}),
);
assert.commandWorked(
coll.getDB().adminCommand({
commitTransaction: 1,
lsid: getLSID(),
txnNumber: getTxnNumber(),
autocommit: false,
}),
);
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.TransactionCommitted);
assert.eq(coll.find().itcount(), 1);
genNextTxnNumber();
},
admin: true,
},
},
abortUnshardCollection: {skip: "does not accept write concern"},
addShard: {skip: "unrelated"},
addShardToZone: {skip: "does not accept write concern"},
aggregate: {
noop: {
// The pipeline will not match any docs, so nothing should be written to "out"
req: {aggregate: collName, pipeline: [{$match: {x: 1}}, {$out: "out"}], cursor: {}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
assert.commandWorked(coll.remove({_id: 1}));
assert.eq(coll.count({_id: 1}), 0);
assert.commandWorked(coll.getDB().createCollection("out"));
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
assert.eq(coll.getDB().out.find().itcount(), 0);
coll.getDB().out.drop();
},
},
success: {
// The pipeline should write a single doc to "out"
req: {aggregate: collName, pipeline: [{$match: {_id: 1}}, {$out: "out"}], cursor: {}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().createCollection("out"));
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().out.find().itcount(), 0);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().out.find().itcount(), 1);
}
assert.eq(coll.count({_id: 1}), 1);
coll.getDB().out.drop();
},
},
failure: {
// Attempt to update _id to the same value and get a duplicate key error
req: {aggregate: collName, pipeline: [{$set: {_id: 0}}, {$out: "out"}], cursor: {}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.insert({_id: 2}));
assert.commandWorked(coll.getDB().createCollection("out"));
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey);
}
assert.eq(coll.getDB().out.find().itcount(), 0);
assert.eq(coll.count({_id: 1}), 1);
coll.getDB().out.drop();
},
},
},
// TODO SPM-2513 Define test case for analyze once the command is user facing
analyze: {skip: "internal only"},
analyzeShardKey: {skip: "does not accept write concern"},
appendOplogNote: {
success: {
// appendOplogNote basic
req: {appendOplogNote: 1, data: {x: 1}},
setupFunc: (coll) => {},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
},
admin: true,
},
},
applyOps: {
noop: {
// 'applyOps' where the update is a no-op
req: {applyOps: [{op: "u", ns: fullNs, o: {_id: 1}, o2: {_id: 1}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.applied, 1);
assert.eq(res.results[0], true);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({_id: 1}), 1);
},
},
success: {
// 'applyOps' basic insert
req: {applyOps: [{op: "i", ns: fullNs, o: {_id: 2, x: 3}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 3}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.applied, 1);
assert.eq(res.results[0], true);
assert.eq(coll.find().itcount(), 2);
assert.eq(coll.count({x: 3}), 2);
},
},
failure: {
// 'applyOps' attempt to update immutable field
req: {applyOps: [{op: "u", ns: fullNs, o: {_id: 2}, o2: {_id: 1}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
},
confirmFunc: (res, coll) => {
assert.eq(res.applied, 1);
assert.eq(res.ok, 0);
assert.eq(res.results[0], false);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({_id: 1}), 1);
},
},
},
authenticate: {skip: "does not accept write concern"},
autoCompact: {skip: "does not accept write concern"},
autoSplitVector: {skip: "does not accept write concern"},
balancerCollectionStatus: {skip: "does not accept write concern"},
balancerStart: {skip: "does not accept write concern"},
balancerStatus: {skip: "does not accept write concern"},
balancerStop: {skip: "does not accept write concern"},
buildInfo: {skip: "does not accept write concern"},
bulkWrite: {
noop: {
// The doc to update doesn't exist
req: {
bulkWrite: 1,
ops: [{update: 0, filter: {_id: 0}, updateMods: {$set: {x: 1}}}],
nsInfo: [{ns: fullNs}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 0);
},
admin: true,
},
success: {
// Basic insert in a bulk write
req: {bulkWrite: 1, ops: [{insert: 0, document: {_id: 2}}], nsInfo: [{ns: fullNs}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 3}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nErrors, 0);
assert.eq(res.nInserted, 1);
},
admin: true,
},
failure: {
// Attempt to insert doc with duplicate _id
req: {bulkWrite: 1, ops: [{insert: 0, document: {_id: 1}}], nsInfo: [{ns: fullNs}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 3}));
},
confirmFunc: (res, coll) => {
assert.eq(res.nErrors, 1);
assert(res.cursor && res.cursor.firstBatch && res.cursor.firstBatch.length == 1);
assert.commandFailedWithCode(res.cursor.firstBatch[0], ErrorCodes.DuplicateKey);
},
admin: true,
},
},
changePrimary: {
noop: {
// The destination shard is already the primary shard for the db
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[0]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Basic change primary
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[1]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard1.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
// Change the primary back
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: cluster.shard0.shardName}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
},
admin: true,
},
},
checkMetadataConsistency: {skip: "does not accept write concern"},
checkShardingIndex: {skip: "does not accept write concern"},
cleanupOrphaned: {skip: "only exist on direct shard connection"},
cleanupReshardCollection: {skip: "does not accept write concern"},
cleanupStructuredEncryptionData: {skip: "does not accept write concern"},
clearJumboFlag: {skip: "does not accept write concern"},
clearLog: {skip: "does not accept write concern"},
cloneCollectionAsCapped: {
success: {
// Basic cloneColectionAsCapped
req: {
cloneCollectionAsCapped: collName,
toCollection: collName + "2",
size: 10 * 1024 * 1024,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.eq(
coll
.getDB()
.getCollection(collName + "2")
.find()
.itcount(),
0,
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
coll
.getDB()
.getCollection(collName + "2")
.find()
.itcount(),
1,
);
coll.getDB()
.getCollection(collName + "2")
.drop();
},
},
failure: {
// The destination collection already exists
req: {
cloneCollectionAsCapped: collName,
toCollection: collName + "2",
size: 10 * 1024 * 1024,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(
coll
.getDB()
.getCollection(collName + "2")
.insert({_id: 1}),
);
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists);
assert.eq(coll.find().itcount(), 1);
},
},
},
clusterAbortTransaction: {skip: "already tested by 'abortTransaction' tests on mongos"},
clusterAggregate: {skip: "already tested by 'aggregate' tests on mongos"},
clusterBulkWrite: {skip: "already tested by 'bulkWrite' tests on mongos"},
clusterCommitTransaction: {skip: "already tested by 'commitTransaction' tests on mongos"},
clusterCount: {skip: "already tested by 'count' tests on mongos"},
clusterDelete: {skip: "already tested by 'delete' tests on mongos"},
clusterFind: {skip: "already tested by 'find' tests on mongos"},
clusterGetMore: {skip: "already tested by 'getMore' tests on mongos"},
clusterInsert: {skip: "already tested by 'insert' tests on mongos"},
clusterUpdate: {skip: "already tested by 'update' tests on mongos"},
collMod: {
noop: {
// Validator already non-existent
req: {collMod: collName, validator: {}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {}}));
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Add validator
req: {collMod: collName, validator: {x: {$exists: true}}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, {x: {$exists: true}});
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
failure: {
// Index to be updated does not exist
req: {collMod: collName, index: {name: "a_1", hidden: true}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
}),
);
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({dropIndexes: collName, index: "a_1"}),
);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.IndexNotFound);
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
collStats: {skip: "does not accept write concern"},
commitReshardCollection: {skip: "does not accept write concern"},
commitShardRemoval: {skip: "unrelated"},
commitTransaction: {
noop: {
// The transaction is already committed
req: () => ({
commitTransaction: 1,
txnNumber: getTxnNumber(),
autocommit: false,
lsid: getLSID(),
}),
setupFunc: (coll) => {
assert.commandWorked(
coll.getDB().runCommand({
insert: collName,
documents: [{_id: 1}],
lsid: getLSID(),
stmtIds: [NumberInt(0)],
txnNumber: getTxnNumber(),
startTransaction: true,
autocommit: false,
}),
);
assert.commandWorked(
coll.getDB().adminCommand({
commitTransaction: 1,
txnNumber: getTxnNumber(),
autocommit: false,
lsid: getLSID(),
}),
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
genNextTxnNumber();
},
admin: true,
},
success: {
// Basic commit transaction
req: () => ({
commitTransaction: 1,
txnNumber: getTxnNumber(),
autocommit: false,
lsid: getLSID(),
}),
setupFunc: (coll) => {
assert.commandWorked(
coll.getDB().runCommand({
insert: collName,
documents: [{_id: 1}],
lsid: getLSID(),
stmtIds: [NumberInt(0)],
txnNumber: getTxnNumber(),
startTransaction: true,
autocommit: false,
}),
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 1);
genNextTxnNumber();
},
admin: true,
},
},
commitTransitionToDedicatedConfigServer: {skip: "unrelated"},
compact: {skip: "does not accept write concern"},
compactStructuredEncryptionData: {skip: "does not accept write concern"},
configureCollectionBalancing: {skip: "does not accept write concern"},
configureFailPoint: {skip: "internal command"},
configureQueryAnalyzer: {skip: "does not accept write concern"},
connPoolStats: {skip: "does not accept write concern"},
connPoolSync: {skip: "internal command"},
connectionStatus: {skip: "does not accept write concern"},
convertToCapped: {
noop: {
// Collection is already capped
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.runCommand({convertToCapped: collName, size: 10 * 1024 * 1024}));
assert.eq(coll.stats().capped, true);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.stats().capped, true);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic convertToCapped
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.eq(coll.stats().capped, false);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.stats().capped, true);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
failure: {
// The collection doesn't exist
req: {convertToCapped: collName, size: 10 * 1024 * 1024},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.NamespaceNotFound);
assert.eq(coll.find().itcount(), 0);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
coordinateCommitTransaction: {skip: "internal command"},
count: {skip: "does not accept write concern"},
cpuload: {skip: "does not accept write concern"},
create: {
noop: {
// Coll already exists
req: {create: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({create: collName}));
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic create coll
req: {create: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
// Ensure DB exists, but create a different collection
assert.commandWorked(coll.getDB().coll2.insert({_id: 1}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
failure: {
// Attempt to create a view and output to a nonexistent collection
req: {create: "viewWithOut", viewOn: collName, pipeline: [{$out: "nonexistentColl"}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.getDB().runCommand({drop: "nonexistentColl"}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandFailedWithCode(res, ErrorCodes.OptionNotSupportedOnView);
}
assert.eq(coll.find().itcount(), 1);
assert(!coll.getDB().getCollectionNames().includes("nonexistentColl"));
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
createIndexes: {
// All voting data bearing nodes are not up for this test. So 'createIndexes' command
// can't succeed with the default index commitQuorum value "votingMembers". So, we run
// createIndexes using commit quorum "majority".
noop: {
// Index already exists
req: {
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
}),
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let details = res;
if ("raw" in details) {
const raw = details.raw;
details = raw[Object.keys(raw)[0]];
}
assert.eq(details.numIndexesBefore, details.numIndexesAfter);
assert.eq(details.note, "all indexes already exist");
},
},
success: {
// Basic create index
req: {
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({b: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let details = res;
if ("raw" in details) {
const raw = details.raw;
details = raw[Object.keys(raw)[0]];
}
assert.eq(details.numIndexesBefore, details.numIndexesAfter - 1);
},
},
failure: {
// Attempt to create two indexes with the same name and different keys
req: {
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({b: 1}));
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {b: -1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.IndexKeySpecsConflict);
},
},
},
createRole: {
targetConfigServer: true,
success: {
// Basic create role
req: {createRole: "foo", privileges: [], roles: []},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
assert.eq(coll.getDB().getRole("foo"), null);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.neq(coll.getDB().getRole("foo"), null);
}
coll.getDB().runCommand({dropRole: "foo"});
},
},
failure: {
// Role already exists
req: {createRole: "foo", privileges: [], roles: []},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.neq(coll.getDB().getRole("foo"), null);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().runCommand({dropUser: "fakeusr"});
} else {
assert.commandFailedWithCode(res, 51002);
}
assert.neq(coll.getDB().getRole("foo"), null);
coll.getDB().runCommand({dropRole: "foo"});
},
},
},
createSearchIndexes: {skip: "does not accept write concern"},
createUnsplittableCollection: {skip: "internal command"},
createUser: {
targetConfigServer: true,
success: {
// Basic create user
req: {createUser: "foo", pwd: "bar", roles: []},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
assert.eq(coll.getDB().getUser("foo"), null);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.neq(coll.getDB().getUser("foo"), null);
}
coll.getDB().runCommand({dropUser: "foo"});
},
},
failure: {
// User already exists
req: {createUser: "foo", pwd: "bar", roles: []},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
assert.neq(coll.getDB().getUser("foo"), null);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs, st) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().runCommand({dropRole: "bar"});
} else {
assert.commandFailedWithCode(res, 51003);
}
assert.neq(coll.getDB().getUser("foo"), null);
coll.getDB().runCommand({dropUser: "foo"});
},
},
},
currentOp: {skip: "does not accept write concern"},
dataSize: {skip: "does not accept write concern"},
dbCheck: {skip: "does not accept write concern"},
dbHash: {skip: "does not accept write concern"},
dbStats: {skip: "does not accept write concern"},
delete: {
noop: {
// The query will not match any doc
req: {delete: collName, deletes: [{q: {_id: 1}, limit: 1}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.remove({_id: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(coll.count({a: 1}), 0);
},
},
success: {
req: {delete: collName, deletes: [{q: {_id: 1}, limit: 1}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 1);
assert.eq(coll.find().itcount(), 0);
},
},
},
distinct: {skip: "does not accept write concern"},
drop: {
noop: {
// The collection has already been dropped
req: {drop: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic drop collection
req: {drop: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({a: 1}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 0);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropAllRolesFromDatabase: {
targetConfigServer: true,
noop: {
// No roles exist
req: {dropAllRolesFromDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
}
},
},
success: {
// Basic dropAllRolesFromDatabase
req: {dropAllRolesFromDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getRoles({rolesInfo: 1}).length, 0);
}
},
},
},
dropAllUsersFromDatabase: {
targetConfigServer: true,
noop: {
// No users exist
req: {dropAllUsersFromDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.eq(coll.getDB().getUsers().length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
assert.eq(coll.getDB().getUsers().length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().runCommand({dropRole: "bar"});
}
},
},
success: {
// Basic dropAllUsersFromDatabase
req: {dropAllUsersFromDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getUsers().length, 0);
}
},
},
},
dropConnections: {skip: "does not accept write concern"},
dropDatabase: {
noop: {
// Database has already been dropped
req: {dropDatabase: 1},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorkedIgnoringWriteConcernErrors(coll.getDB().runCommand({dropDatabase: 1}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
},
},
success: {
// Basic drop database
req: {dropDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({a: 1}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropIndexes: {
noop: {
// Passing "*" will drop all indexes except the _id index. The only index on this
// collection is the _id index, so the command will be a no-op.
req: {dropIndexes: collName, index: "*"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
// Implicitly create the collection via insert if it doesn't exist
assert.commandWorked(coll.insert({b: "b"}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
optionalArgs.numIndexesBefore = coll.getIndexes().length;
// Make a non-acknowledged write so that the no-op will have to fail with WCE
assert.commandWorkedIgnoringWriteConcernErrors(
coll.insert({b: "b"}, {writeConcern: {w: 3, wtimeout: 100}}),
);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic drop index
req: {
dropIndexes: collName,
index: "b_1",
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({b: "b"}));
const numIndexesBefore = coll.getIndexes().length;
optionalArgs.numIndexesBefore = numIndexesBefore;
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
assert.eq(coll.getIndexes().length, numIndexesBefore + 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let details = res;
if ("raw" in details) {
const raw = details.raw;
details = raw[Object.keys(raw)[0]];
}
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropRole: {
targetConfigServer: true,
success: {
// Basic dropRole
req: {dropRole: "foo"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.eq(coll.getDB().getRoles().length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getRoles().length, 0);
}
},
},
failure: {
// Role does not exist
req: {dropRole: "foo"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.eq(coll.getDB().getRoles().length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().runCommand({dropRole: "bar"});
}
assert.eq(coll.getDB().getRoles().length, 0);
},
},
},
dropSearchIndex: {skip: "does not accept write concern"},
dropUser: {
targetConfigServer: true,
success: {
// Basic dropUser
req: {dropUser: "foo"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
assert.eq(coll.getDB().getUsers().length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getUsers().length, 0);
}
},
},
failure: {
// User doesn't exist
req: {dropUser: "foo"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.eq(coll.getDB().getUsers({filter: {user: "foo"}}).length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.UserNotFound);
assert.eq(coll.getDB().getUsers({filter: {user: "foo"}}).length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().runCommand({dropRole: "bar"});
}
},
},
},
echo: {skip: "does not accept write concern"},
enableSharding: {
targetConfigServer: true,
noop: {
// Database creation only acknowledged by the config primary node
req: {enableSharding: dbName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding no
// longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
// `enableSharding` throws the WCE as top-level error when majority WC is not
// available
assert.commandFailedWithCode(
coll.getDB().adminCommand({enableSharding: dbName}),
ErrorCodes.WriteConcernTimeout,
);
// The database exists on the config primary node
assert.eq(
coll
.getDB()
.getSiblingDB("config")
.databases.find(
{_id: dbName},
{}, // project
{readConcern: {level: 1}, $readPreference: {mode: "primary"}},
)
.itcount(),
1,
);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
success: {
// Basic enable sharding
req: {enableSharding: dbName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 0);
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding no
// longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
},
endSessions: {skip: "does not accept write concern"},
eseRotateActiveKEK: {skip: "does not accept write concern"},
explain: {skip: "does not accept write concern"},
features: {skip: "does not accept write concern"},
filemd5: {skip: "does not accept write concern"},
find: {skip: "does not accept write concern"},
findAndModify: {
noop: {
// The doc to update does not exist
req: {findAndModify: collName, query: {_id: 1}, update: {b: 2}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.remove({_id: 1}));
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find().itcount(), 0);
assert.eq(coll.count({b: 2}), 0);
},
},
success: {
// Basic findOneAndReplace
req: {findAndModify: collName, query: {_id: 1}, update: {_id: 1, b: 2}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find().itcount(), 1);
},
},
failure: {
// Attempt to update and cause document validation error
req: {findAndModify: collName, query: {_id: 1}, update: {_id: 1}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({_id: 1}), 1);
},
},
},
flushRouterConfig: {skip: "does not accept write concern"},
forceerror: {skip: "test command"},
fsync: {skip: "does not accept write concern"},
fsyncUnlock: {skip: "does not accept write concern"},
getAuditConfig: {skip: "does not accept write concern"},
getChangeStreamState: {skip: "removed in v8.3"},
getClusterParameter: {skip: "does not accept write concern"},
getCmdLineOpts: {skip: "does not accept write concern"},
getDatabaseVersion: {skip: "internal command"},
getDefaultRWConcern: {skip: "does not accept write concern"},
getDiagnosticData: {skip: "does not accept write concern"},
getESERotateActiveKEKStatus: {skip: "does not accept write concern"},
getLog: {skip: "does not accept write concern"},
getMore: {skip: "does not accept write concern"},
getParameter: {skip: "does not accept write concern"},
getQueryableEncryptionCountInfo: {skip: "does not accept write concern"},
getShardMap: {skip: "internal command"},
getShardVersion: {skip: "internal command"},
getTransitionToDedicatedConfigServerStatus: {skip: "unrelated"},
godinsert: {skip: "for testing only"},
grantPrivilegesToRole: {
targetConfigServer: true,
noop: {
// Role already has privilege
req: {
grantPrivilegesToRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
grantPrivilegesToRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
}),
);
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 1);
assert.eq(role[0].privileges[0].actions, ["find"]);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 1);
assert.eq(role[0].privileges[0].actions, ["find"]);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
},
},
success: {
// Basic grantPrivilegesToRole
req: {
grantPrivilegesToRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 1);
assert.eq(role[0].privileges[0].actions, ["find"]);
}
coll.getDB().dropRole("foo");
},
},
},
grantRolesToRole: {
targetConfigServer: true,
noop: {
// Foo already has role bar
req: {grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
createRole: "bar",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
roles: [],
}),
);
assert.commandWorked(
coll.getDB().runCommand({grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]}),
);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 1);
assert.eq(role.inheritedRoles[0].role, "bar");
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 1);
assert.eq(role.inheritedRoles[0].role, "bar");
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
coll.getDB().dropRole("bar");
},
},
success: {
// Basic grantRolesToRole
req: {grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
createRole: "bar",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
roles: [],
}),
);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 1);
assert.eq(role.inheritedRoles[0].role, "bar");
}
coll.getDB().dropRole("foo");
coll.getDB().dropRole("bar");
},
},
},
grantRolesToUser: {
targetConfigServer: true,
noop: {
// User already has role
req: {grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
assert.commandWorked(
coll.getDB().runCommand({grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]}),
);
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropRole("bar");
}
coll.getDB().dropRole("foo");
coll.getDB().runCommand({dropUser: "foo"});
},
},
success: {
// Basic grantRolesToUser
req: {grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
}
coll.getDB().dropRole("foo");
coll.getDB().runCommand({dropUser: "foo"});
},
},
failure: {
// Role does not exist
req: {grantRolesToUser: "foo", roles: ["fakeRole"]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
}
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropRole("bar");
}
coll.getDB().dropRole("foo");
coll.getDB().runCommand({dropUser: "foo"});
},
},
},
getTrafficRecordingStatus: {skip: "does not accept write concern"},
handshake: {skip: "does not accept write concern"},
hello: {skip: "does not accept write concern"},
hostInfo: {skip: "does not accept write concern"},
httpClientRequest: {skip: "does not accept write concern"},
exportCollection: {skip: "internal command"},
importCollection: {skip: "internal command"},
insert: {
// A no-op insert that returns success is not possible
success: {
// Basic insert
req: {insert: collName, documents: [{_id: 11}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 10}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 1);
assert.eq(coll.find().itcount(), 2);
},
},
failure: {
// Insert causes DuplicateKeyError
req: {insert: collName, documents: [{_id: 10}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 10}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.DuplicateKey);
assert.eq(res.n, 0);
assert.eq(coll.count({_id: 10}), 1);
},
},
},
internalRenameIfOptionsAndIndexesMatch: {skip: "internal command"},
invalidateUserCache: {skip: "does not accept write concern"},
isdbgrid: {skip: "does not accept write concern"},
isMaster: {skip: "does not accept write concern"},
killAllSessions: {skip: "does not accept write concern"},
killAllSessionsByPattern: {skip: "does not accept write concern"},
killCursors: {skip: "does not accept write concern"},
killOp: {skip: "does not accept write concern"},
killSessions: {skip: "does not accept write concern"},
listCollections: {skip: "does not accept write concern"},
listCommands: {skip: "does not accept write concern"},
listDatabases: {skip: "does not accept write concern"},
listDatabasesForAllTenants: {skip: "does not accept write concern"},
listIndexes: {skip: "does not accept write concern"},
listSearchIndexes: {skip: "does not accept write concern"},
listShards: {skip: "does not accept write concern"},
lockInfo: {skip: "does not accept write concern"},
logApplicationMessage: {skip: "does not accept write concern"},
logMessage: {skip: "does not accept write concern"},
logRotate: {skip: "does not accept write concern"},
logout: {skip: "does not accept write concern"},
makeSnapshot: {skip: "does not accept write concern"},
mapReduce: {skip: "deprecated"},
mergeAllChunksOnShard: {skip: "does not accept write concern"},
mergeChunks: {skip: "does not accept write concern"},
moveChunk: {
targetConfigServer: true,
success: {
// Basic moveChunk
req: (cluster, coll) => ({
moveChunk: fullNs,
find: getShardKeyMinRanges(coll)[0]["min"],
to: getShardNames(cluster)[0],
secondaryThrottle: true,
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
optionalArgs.originalShard = coll
.getDB()
.getSiblingDB("config")
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
let destShard = getShardNames(cluster)[1];
assert.commandWorked(coll.getDB().adminCommand({moveChunk: fullNs, find: keyToMove, to: destShard}));
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
destShard,
);
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
// longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
getShardNames(cluster)[1],
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
assert.commandWorked(
coll.getDB().adminCommand({moveChunk: fullNs, find: keyMoved, to: optionalArgs.originalShard}),
);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
optionalArgs.originalShard,
);
},
admin: true,
},
},
moveCollection: {skip: "does not accept write concern"},
movePrimary: {
noop: {
// The destination shard is already the primary shard for the db
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[0]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Basic movePrimary
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[1]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
// Change the primary back
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: cluster.shard0.shardName}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
},
admin: true,
},
},
moveRange: {
targetConfigServer: true,
noop: {
// Chunk already lives on this shard
req: (cluster, coll) => ({
moveRange: fullNs,
min: getShardKeyMinRanges(coll)[0]["min"],
toShard: getShardNames(cluster)[0],
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
optionalArgs.originalShard = coll
.getDB()
.getSiblingDB("config")
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
let destShard = getShardNames(cluster)[0];
assert.commandWorked(
coll.getDB().adminCommand({moveRange: fullNs, min: keyToMove, toShard: destShard}),
);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
destShard,
);
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
// longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
getShardNames(cluster)[0],
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
assert.commandWorked(
coll.getDB().adminCommand({moveRange: fullNs, min: keyMoved, toShard: optionalArgs.originalShard}),
);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
optionalArgs.originalShard,
);
},
admin: true,
},
success: {
// Basic moveRange
req: (cluster, coll) => ({
moveRange: fullNs,
min: getShardKeyMinRanges(coll)[0]["min"],
toShard: getShardNames(cluster)[0],
secondaryThrottle: true,
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let keyToMove = getShardKeyMinRanges(coll)[0]["min"];
optionalArgs.originalShard = coll
.getDB()
.getSiblingDB("config")
.chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard;
let destShard = getShardNames(cluster)[1];
assert.commandWorked(
coll.getDB().adminCommand({moveRange: fullNs, min: keyToMove, toShard: destShard}),
);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyToMove}).shard,
destShard,
);
// TODO SERVER-97754 Do not stop the remaining secondary once moveChunk no
// longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
let keyMoved = getShardKeyMinRanges(coll)[0]["min"];
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
getShardNames(cluster)[1],
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
assert.commandWorked(
coll.getDB().adminCommand({moveRange: fullNs, min: keyMoved, toShard: optionalArgs.originalShard}),
);
assert.eq(
coll.getDB().getSiblingDB("config").chunks.findOne({uuid: coll.getUUID(), min: keyMoved}).shard,
optionalArgs.originalShard,
);
},
admin: true,
},
},
multicast: {skip: "does not accept write concern"},
netstat: {skip: "internal command"},
oidcListKeys: {skip: "does not accept write concern"},
oidcRefreshKeys: {skip: "does not accept write concern"},
pinHistoryReplicated: {skip: "internal command"},
ping: {skip: "does not accept write concern"},
planCacheClear: {skip: "does not accept write concern"},
planCacheClearFilters: {skip: "does not accept write concern"},
planCacheListFilters: {skip: "does not accept write concern"},
planCacheSetFilter: {skip: "does not accept write concern"},
prepareTransaction: {skip: "internal command"},
profile: {skip: "does not accept write concern"},
reIndex: {skip: "does not accept write concern"},
reapLogicalSessionCacheNow: {skip: "does not accept write concern"},
refineCollectionShardKey: {
noop: {
// Refine to same shard key
req: (cluster, coll) => ({refineCollectionShardKey: fullNs, key: getShardKey(coll, fullNs)}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({x: 1}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Add additional field to shard key
req: (cluster, coll) => ({
refineCollectionShardKey: fullNs,
key: Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let sk = getShardKey(coll, fullNs);
optionalArgs.origSk = sk;
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), sk), 0);
assert.commandWorked(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [
{
key: Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
name: "sk_1",
},
],
commitQuorum: "majority",
}),
);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
bsonWoCompare(
getShardKey(coll, fullNs),
Object.assign({}, getShardKey(coll, fullNs), {newSkField: 1}),
),
0,
);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
assert.commandWorked(
coll.getDB().adminCommand({refineCollectionShardKey: fullNs, key: optionalArgs.sk}),
);
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), optionalArgs.sk), 0);
},
admin: true,
},
},
refreshLogicalSessionCacheNow: {skip: "does not accept write concern"},
refreshSessions: {skip: "does not accept write concern"},
releaseMemory: {skip: "does not accept write concern"},
removeShard: {skip: "unrelated"},
removeShardFromZone: {skip: "does not accept write concern"},
renameCollection: {
success: {
// Basic rename
req: {
renameCollection: fullNs,
to: dbName + "." + "coll2",
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.eq(coll.find().itcount(), 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 0);
assert.eq(coll.getDB().coll2.find().itcount(), 1);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
coll.getDB().coll2.drop();
},
admin: true,
},
failure: {
// target collection exists, and dropTarget not set to true
req: {
renameCollection: fullNs,
to: dbName + "." + "coll2",
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.getDB().coll2.insert({a: 1}));
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.getDB().coll2.find().itcount(), 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.NamespaceExists);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.getDB().coll2.find().itcount(), 1);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
},
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
replSetFreeze: {skip: "does not accept write concern"},
replSetGetConfig: {skip: "does not accept write concern"},
replSetGetRBID: {skip: "does not accept write concern"},
replSetGetStatus: {skip: "does not accept write concern"},
replSetHeartbeat: {skip: "does not accept write concern"},
replSetInitiate: {skip: "does not accept write concern"},
replSetMaintenance: {skip: "does not accept write concern"},
replSetReconfig: {skip: "does not accept write concern"},
replSetRequestVotes: {skip: "does not accept write concern"},
replSetResizeOplog: {skip: "does not accept write concern"},
replSetStepDown: {skip: "does not accept write concern"},
replSetStepUp: {skip: "does not accept write concern"},
replSetSyncFrom: {skip: "does not accept write concern"},
replSetTest: {skip: "does not accept write concern"},
replSetTestEgress: {skip: "does not accept write concern"},
replSetUpdatePosition: {skip: "does not accept write concern"},
resetPlacementHistory: {skip: "internal command"},
reshardCollection: {skip: "does not accept write concern"},
rewriteCollection: {skip: "does not accept write concern"},
revokePrivilegesFromRole: {
targetConfigServer: true,
noop: {
// Role does not have privilege
req: {
revokePrivilegesFromRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.getDB().runCommand({
createRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
roles: [],
}),
);
assert.commandWorked(
coll.getDB().runCommand({
revokePrivilegesFromRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["insert"]}],
}),
);
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
},
},
success: {
// Basic revokePrivilegesFromRole
req: {
revokePrivilegesFromRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
grantPrivilegesToRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
}),
);
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let role = coll.getDB().getRoles({rolesInfo: 1, showPrivileges: true});
assert.eq(role.length, 1);
assert.eq(role[0].privileges.length, 0);
}
coll.getDB().dropRole("foo");
},
},
},
revokeRolesFromRole: {
targetConfigServer: true,
noop: {
// Role foo does not have role bar
req: {revokeRolesFromRole: "foo", roles: [{role: "bar", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
createRole: "bar",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
roles: [],
}),
);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
coll.getDB().dropRole("bar");
},
},
success: {
// Basic revokeRolesFromRole
req: {revokeRolesFromRole: "foo", roles: [{role: "bar", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(
coll.getDB().runCommand({
createRole: "bar",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
roles: [],
}),
);
assert.commandWorked(
coll.getDB().runCommand({grantRolesToRole: "foo", roles: [{role: "bar", db: dbName}]}),
);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 1);
assert.eq(role.inheritedRoles[0].role, "bar");
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 0);
}
coll.getDB().dropRole("foo");
coll.getDB().dropRole("bar");
},
},
},
revokeRolesFromUser: {
targetConfigServer: true,
noop: {
// User does not have role to revoke
req: {revokeRolesFromUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropRole("bar");
}
coll.getDB().dropRole("foo");
coll.getDB().runCommand({dropUser: "foo"});
},
},
success: {
// Basic revokeRolesFromUser
req: {revokeRolesFromUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "bar", roles: []}));
assert.commandWorked(
coll.getDB().runCommand({grantRolesToUser: "foo", roles: [{role: "foo", db: dbName}]}),
);
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
}
coll.getDB().dropRole("foo");
coll.getDB().runCommand({dropUser: "foo"});
},
},
},
rolesInfo: {skip: "does not accept write concern"},
rotateCertificates: {skip: "does not accept write concern"},
rotateFTDC: {skip: "does not accept write concern"},
saslContinue: {skip: "does not accept write concern"},
saslStart: {skip: "does not accept write concern"},
sbe: {skip: "internal command"},
serverStatus: {skip: "does not accept write concern"},
setAllowMigrations: {
targetConfigServer: true,
noop: {
// Migrations already not allowed
req: {setAllowMigrations: fullNs, allowMigrations: false},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().adminCommand({setAllowMigrations: fullNs, allowMigrations: false}));
assert.eq(
coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}).permitMigrations,
false,
);
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
coll.getDB().getSiblingDB("config").collections.findOne({_id: fullNs}).permitMigrations,
false,
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
success: {
// Basic setAllowMigrations
req: {setAllowMigrations: fullNs, allowMigrations: false},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().adminCommand({setAllowMigrations: fullNs, allowMigrations: true}));
assert.eq(
coll
.getDB()
.getSiblingDB("config")
.collections.find({_id: fullNs, permitMigrations: {$exists: false}})
.itcount(),
1,
);
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
coll
.getDB()
.getSiblingDB("config")
.collections.find({_id: fullNs, permitMigrations: {$exists: false}})
.itcount(),
0,
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
},
setAuditConfig: {skip: "does not accept write concern"},
setCommittedSnapshot: {skip: "internal command"},
setDefaultRWConcern: {
targetConfigServer: true,
noop: {
// Default write concern already set to w:1
req: {setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.getDB().adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}}),
);
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
"w": 1,
"wtimeout": 0,
});
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
"w": 1,
"wtimeout": 0,
});
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
}
// Reset to default of majority
assert.commandWorked(
coll.getDB().adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
}),
);
},
admin: true,
},
success: {
// Default RWConcern has wtimeout of 1234
req: {setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 0}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll
.getDB()
.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 1234}}),
);
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
"w": 1,
"wtimeout": 1234,
});
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
}
// Reset to default of majority
assert.commandWorked(
coll.getDB().adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
}),
);
},
admin: true,
},
failure: {
// Default write concern cannot be unset once it is set
req: {setDefaultRWConcern: 1, defaultWriteConcern: {}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll
.getDB()
.adminCommand({setDefaultRWConcern: 1, defaultWriteConcern: {"w": 1, "wtimeout": 1234}}),
);
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
// Unacknowledged write on the CSRS to force a WCE upon command failure
assert.commandWorkedIgnoringWriteConcernErrors(
coll
.getDB()
.getSiblingDB("config")
.tmp.insert({x: 1}, {writeConcern: {w: "majority", wtimeout: 1000}}),
);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.IllegalOperation);
assert.eq(coll.getDB().adminCommand({getDefaultRWConcern: 1}).defaultWriteConcern, {
"w": 1,
"wtimeout": 1234,
});
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
}
// Reset to default of majority
assert.commandWorked(
coll.getDB().adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: {"w": "majority", "wtimeout": 0},
}),
);
},
admin: true,
},
},
setFeatureCompatibilityVersion: {
targetConfigServer: true,
noop: {
// FCV already 'latest'
req: {setFeatureCompatibilityVersion: latestFCV, confirm: true},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let fcv = coll
.getDB()
.getSiblingDB("admin")
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version;
optionalArgs.fcv = fcv;
assert.commandWorked(
coll.getDB().adminCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}),
);
assert.eq(
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
.version,
latestFCV,
);
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
.version,
latestFCV,
);
assert.commandWorked(
coll.getDB().adminCommand({setFeatureCompatibilityVersion: optionalArgs.fcv, confirm: true}),
);
assert.eq(
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
.version,
optionalArgs.fcv,
);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
}
},
admin: true,
},
success: {
// Change FCV from lastLTS to latest
req: {setFeatureCompatibilityVersion: latestFCV, confirm: true},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let fcv = coll
.getDB()
.getSiblingDB("admin")
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version;
optionalArgs.fcv = fcv;
assert.commandWorked(
coll.getDB().adminCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}),
);
assert.eq(
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
.version,
lastLTSFCV,
);
// TODO SERVER-97754 Do not stop the remaining secondary once setAllowMigrations
// no longer override user provided writeConcern
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(
coll
.getDB()
.getSiblingDB("admin")
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version,
lastLTSFCV,
);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
coll
.getDB()
.getSiblingDB("admin")
.system.version.findOne({"_id": "featureCompatibilityVersion"}).version,
latestFCV,
);
}
// Reset FCV
assert.commandWorked(
coll.getDB().adminCommand({setFeatureCompatibilityVersion: optionalArgs.fcv, confirm: true}),
);
assert.eq(
coll.getDB().getSiblingDB("admin").system.version.findOne({"_id": "featureCompatibilityVersion"})
.version,
optionalArgs.fcv,
);
},
admin: true,
},
},
setProfilingFilterGlobally: {skip: "does not accept write concern"},
setIndexCommitQuorum: {
noop: {
// commitQuorum already majority
req: {setIndexCommitQuorum: collName, indexNames: ["b_1"], commitQuorum: "majority"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1, b: 1}));
let fpConn = coll.getDB();
if (clusterType == "sharded") {
// Make sure we set the fp on a shard that will be sent the createIndex request
let owningShard = cluster.getShard(coll, {_id: 1, b: 1}, false /* includeEmpty */);
fpConn = owningShard.getDB(dbName);
}
optionalArgs.failpoint = configureFailPoint(fpConn, "hangAfterIndexBuildFirstDrain");
optionalArgs.thread = new Thread(
(host, dbName, collName) => {
const conn = new Mongo(host);
assert.commandWorked(
conn.getDB(dbName).runCommand({
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
},
coll.getDB().getMongo().host,
dbName,
collName,
);
optionalArgs.thread.start();
optionalArgs.failpoint["wait"]();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (res.raw) {
Object.keys(res.raw).forEach((key) => {
assert.commandWorkedIgnoringWriteConcernErrorsOrFailedWithCode(
res.raw[key],
ErrorCodes.IndexNotFound,
);
});
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
optionalArgs.failpoint["off"]();
optionalArgs.thread.join();
},
},
success: {
// Basic setIndexCommitQuorum
req: {setIndexCommitQuorum: collName, indexNames: ["b_1"], commitQuorum: 2},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({_id: 1}));
let fpConn = coll.getDB();
if (clusterType == "sharded") {
// Make sure we set the fp on a shard that will be sent the createIndex request
let owningShard = cluster.getShard(coll, {_id: 1} /* includeEmpty */);
fpConn = owningShard.getDB(dbName);
}
optionalArgs.failpoint = configureFailPoint(fpConn, "hangAfterIndexBuildFirstDrain");
optionalArgs.thread = new Thread(
(host, dbName, collName) => {
// Use the index builds coordinator for a two-phase index build.
const conn = new Mongo(host);
assert.commandWorked(
conn.getDB(dbName).runCommand({
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
},
coll.getDB().getMongo().host,
dbName,
collName,
);
optionalArgs.thread.start();
optionalArgs.failpoint["wait"]();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (res.raw) {
Object.keys(res.raw).forEach((key) => {
assert.commandWorkedIgnoringWriteConcernErrorsOrFailedWithCode(
res.raw[key],
ErrorCodes.IndexNotFound,
);
});
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
optionalArgs.failpoint["off"]();
optionalArgs.thread.join();
},
},
},
setParameter: {skip: "does not accept write concern"},
setShardVersion: {skip: "internal command"},
setChangeStreamState: {skip: "removed in v8.3"},
setClusterParameter: {skip: "does not accept write concern"},
setQuerySettings: {skip: "does not accept write concern"},
removeQuerySettings: {skip: "does not accept write concern"},
setUserWriteBlockMode: {skip: "does not accept write concern"},
shardCollection: {
noop: {
// Coll already sharded
req: {shardCollection: fullNs, key: {x: 1}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().adminCommand({shardCollection: fullNs, key: {x: 1}}));
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {x: 1}), 0);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(getShardKey(coll, fullNs), {x: 1});
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Basic shard coll
req: {shardCollection: fullNs, key: {x: 1}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
// Ensure DB exists, but create a different collection
assert.commandWorked(coll.getDB().coll2.insert({_id: 1}));
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {}), 0);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), {x: 1}), 0);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
},
shardDrainingStatus: {skip: "unrelated"},
shardingState: {skip: "does not accept write concern"},
shutdown: {skip: "does not accept write concern"},
sleep: {skip: "does not accept write concern"},
split: {skip: "does not accept write concern"},
splitChunk: {skip: "does not accept write concern"},
splitVector: {skip: "internal command"},
stageDebug: {skip: "does not accept write concern"},
startSession: {skip: "does not accept write concern"},
startShardDraining: {skip: "unrelated"},
startRecordingTraffic: {skip: "renamed to startTrafficRecording"},
stopRecordingTraffic: {skip: "renamed to stopTrafficRecording"},
stopShardDraining: {skip: "unrelated"},
startTrafficRecording: {skip: "does not accept write concern"},
startTransitionToDedicatedConfigServer: {skip: "unrelated"},
stopTrafficRecording: {skip: "does not accept write concern"},
stopTransitionToDedicatedConfigServer: {skip: "unrelated"},
sysprofile: {skip: "internal command"},
testCommandFeatureFlaggedOnLatestFCV83: {skip: "internal command"},
testDeprecation: {skip: "test command"},
testDeprecationInVersion2: {skip: "test command"},
testInternalTransactions: {skip: "internal command"},
testRemoval: {skip: "test command"},
testReshardCloneCollection: {skip: "internal command"},
testVersions1And2: {skip: "test command"},
testVersion2: {skip: "test command"},
timeseriesCatalogBucketParamsChanged: {skip: "internal command"},
top: {skip: "does not accept write concern"},
transitionFromDedicatedConfigServer: {skip: "unrelated"},
transitionToDedicatedConfigServer: {skip: "unrelated"},
transitionToShardedCluster: {skip: "internal command"},
unshardCollection: {skip: "does not accept write concern"},
untrackUnshardedCollection: {skip: "does not accept write concern"},
update: {
noop: {
// The query will not match any doc
req: {update: collName, updates: [{q: {_id: 1}, u: {b: 2}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 0, b: 2}));
assert.commandWorked(coll.remove({_id: 0}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
assert.eq(coll.find().itcount(), 0);
},
},
success: {
// Basic update
req: {update: collName, updates: [{q: {_id: 1}, u: {$set: {b: 1}}}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert([{_id: 1}]));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find({b: 1}).toArray().length, 1);
},
},
failure: {
// Attempt to update a doc that would fail the validator
req: {update: collName, updates: [{q: {_id: 1}, u: {_id: 1, c: 2}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1, x: 1}));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0]["code"], ErrorCodes.DocumentValidationFailure);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
assert.eq(coll.count({_id: 1}), 1);
},
},
},
updateRole: {
targetConfigServer: true,
noop: {
// updateRole to have privileges it already has
req: {
updateRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.getDB().runCommand({
createRole: "foo",
privileges: [{resource: {db: dbName, collection: collName}, actions: ["find"]}],
roles: [],
}),
);
assert.eq(coll.getDB().getRoles().length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
assert.eq(coll.getDB().getRoles().length, 1);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
},
},
success: {
// Basic updateRole to add inherited role
req: {updateRole: "foo", roles: ["bar"]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createRole: "bar", privileges: [], roles: []}));
assert.eq(coll.getDB().getRoles().length, 2);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let role = coll.getDB().getRole("foo");
assert.eq(role.inheritedRoles.length, 1);
assert.eq(role.inheritedRoles[0]["role"], ["bar"]);
}
coll.getDB().dropRole("foo");
coll.getDB().dropRole("bar");
},
},
failure: {
// Creating cycle
req: {updateRole: "foo", roles: ["foo"]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.eq(coll.getDB().getRoles().length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createUser: "fakeusr",
pwd: "bar",
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.InvalidRoleModification);
assert.eq(coll.getDB().getRoles().length, 1);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropUser("fakeusr");
}
coll.getDB().dropRole("foo");
},
},
},
updateSearchIndex: {skip: "does not accept write concern"},
updateUser: {
targetConfigServer: true,
noop: {
// user already has role
req: {updateUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: ["foo"]}));
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropRole("bar");
}
coll.getDB().dropUser("foo");
coll.getDB().dropRole("foo");
},
},
success: {
// Basic updateUser to cadd role
req: {updateUser: "foo", roles: [{role: "foo", db: dbName}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createRole: "foo", privileges: [], roles: []}));
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: []}));
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 0);
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let user = coll.getDB().getUser("foo");
assert.eq(user.roles.length, 1);
}
coll.getDB().dropUser("foo");
coll.getDB().dropRole("foo");
},
},
failure: {
// Role does not exist
req: {updateUser: "foo", roles: ["fakeRole"]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({createUser: "foo", pwd: "pwd", roles: []}));
// UMCs enforce wc: majority, so shut down the other node
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncLock();
// Run this to advance the system optime on the config server, so that the
// subsequent failing request will also encounter a WriteConcernTimeout.
assert.commandFailedWithCode(
coll.getDB().runCommand({
createRole: "bar",
privileges: [],
roles: [],
writeConcern: {w: "majority", wtimeout: 100},
}),
ErrorCodes.WriteConcernTimeout,
);
}
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.RoleNotFound);
assert.eq(coll.getDB().getUser("foo").roles.length, 0);
if (clusterType == "sharded") {
secondariesRunning[0].getDB("admin").fsyncUnlock();
coll.getDB().dropRole("bar");
}
coll.getDB().dropUser("foo");
},
},
},
updateZoneKeyRange: {skip: "does not accept write concern"},
usersInfo: {skip: "does not accept write concern"},
validate: {skip: "does not accept write concern"},
validateDBMetadata: {skip: "does not accept write concern"},
voteAbortIndexBuild: {skip: "internal command"},
voteCommitImportCollection: {skip: "internal command"},
voteCommitIndexBuild: {skip: "internal command"},
waitForFailPoint: {skip: "test command"},
getShardingReady: {skip: "internal command"},
whatsmysni: {skip: "does not accept write concern"},
whatsmyuri: {skip: "internal command"},
};
// All commands applicable on timeseries views in the server.
const wcTimeseriesViewsCommandsTests = {
_addShard: {skip: "internal command"},
_cloneCollectionOptionsFromPrimaryShard: {skip: "internal command"},
_clusterQueryWithoutShardKey: {skip: "internal command"},
_clusterWriteWithoutShardKey: {skip: "internal command"},
_configsvrAbortReshardCollection: {skip: "internal command"},
_configsvrAddShard: {skip: "internal command"},
_configsvrAddShardToZone: {skip: "internal command"},
_configsvrBalancerCollectionStatus: {skip: "internal command"},
_configsvrBalancerStart: {skip: "internal command"},
_configsvrBalancerStatus: {skip: "internal command"},
_configsvrBalancerStop: {skip: "internal command"},
_configsvrCheckClusterMetadataConsistency: {skip: "internal command"},
_configsvrCheckMetadataConsistency: {skip: "internal command"},
_configsvrCleanupReshardCollection: {skip: "internal command"},
_configsvrCollMod: {skip: "internal command"},
_configsvrClearJumboFlag: {skip: "internal command"},
_configsvrCommitChunksMerge: {skip: "internal command"},
_configsvrCommitChunkMigration: {skip: "internal command"},
_configsvrCommitChunkSplit: {skip: "internal command"},
_configsvrCommitMergeAllChunksOnShard: {skip: "internal command"},
_configsvrCommitMovePrimary: {skip: "internal command"},
_configsvrCommitRefineCollectionShardKey: {skip: "internal command"},
_configsvrCommitReshardCollection: {skip: "internal command"},
_configsvrCommitShardRemoval: {skip: "internal command"},
_configsvrConfigureCollectionBalancing: {skip: "internal command"},
_configsvrCreateDatabase: {skip: "internal command"},
_configsvrEnsureChunkVersionIsGreaterThan: {skip: "internal command"},
_configsvrGetHistoricalPlacement: {skip: "internal command"},
_configsvrMoveRange: {skip: "internal command"},
_configsvrRemoveChunks: {skip: "internal command"},
_configsvrRemoveShard: {skip: "internal command"},
_configsvrRemoveShardCommit: {skip: "internal command"},
_configsvrRemoveShardFromZone: {skip: "internal command"},
_configsvrRemoveTags: {skip: "internal command"},
_configsvrRenameCollection: {skip: "internal command"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
_configsvrResetPlacementHistory: {skip: "internal command"},
_configsvrReshardCollection: {skip: "internal command"},
_configsvrRunRestore: {skip: "internal command"},
_configsvrSetAllowMigrations: {skip: "internal command"},
_configsvrSetClusterParameter: {skip: "internal command"},
_configsvrSetUserWriteBlockMode: {skip: "internal command"},
_configsvrShardDrainingStatus: {skip: "internal command"},
_configsvrStartShardDraining: {skip: "internal command"},
_configsvrStopShardDraining: {skip: "internal command"},
_configsvrTransitionFromDedicatedConfigServer: {skip: "internal command"},
_configsvrTransitionToDedicatedConfigServer: {skip: "internal command"},
_configsvrUpdateZoneKeyRange: {skip: "internal command"},
_dropConnectionsToMongot: {skip: "internal command"},
_dropMirrorMaestroConnections: {skip: "internal command"},
_flushDatabaseCacheUpdates: {skip: "internal command"},
_flushDatabaseCacheUpdatesWithWriteConcern: {skip: "internal command"},
_flushReshardingStateChange: {skip: "internal command"},
_flushRoutingTableCacheUpdates: {skip: "internal command"},
_flushRoutingTableCacheUpdatesWithWriteConcern: {skip: "internal command"},
_getNextSessionMods: {skip: "internal command"},
_getUserCacheGeneration: {skip: "internal command"},
_hashBSONElement: {skip: "internal command"},
_isSelf: {skip: "internal command"},
_killOperations: {skip: "internal command"},
_mergeAuthzCollections: {skip: "internal command"},
_migrateClone: {skip: "internal command"},
_mirrorMaestroConnPoolStats: {skip: "internal command"},
_mongotConnPoolStats: {skip: "internal command"},
_recvChunkAbort: {skip: "internal command"},
_recvChunkCommit: {skip: "internal command"},
_recvChunkReleaseCritSec: {skip: "internal command"},
_recvChunkStart: {skip: "internal command"},
_recvChunkStatus: {skip: "internal command"},
_refreshQueryAnalyzerConfiguration: {skip: "internal command"},
_shardsvrAbortReshardCollection: {skip: "internal command"},
_shardsvrBeginMigrationBlockingOperation: {skip: "internal command"},
_shardsvrChangePrimary: {skip: "internal command"},
_shardsvrCleanupReshardCollection: {skip: "internal command"},
_shardsvrCloneCatalogData: {skip: "internal command"},
_shardsvrCommitToShardLocalCatalog: {skip: "internal command"},
_shardsvrCheckMetadataConsistency: {skip: "internal command"},
_shardsvrCheckMetadataConsistencyParticipant: {skip: "internal command"},
_shardsvrCleanupStructuredEncryptionData: {skip: "internal command"},
_shardsvrCloneAuthoritativeMetadata: {skip: "internal command"},
_shardsvrCommitCreateDatabaseMetadata: {skip: "internal command"},
_shardsvrCommitDropDatabaseMetadata: {skip: "internal command"},
_shardsvrCommitReshardCollection: {skip: "internal command"},
_shardsvrCompactStructuredEncryptionData: {skip: "internal command"},
_shardsvrConvertToCapped: {skip: "internal command"},
_shardsvrCoordinateMultiUpdate: {skip: "internal command"},
_shardsvrCreateCollection: {skip: "internal command"},
_shardsvrCreateCollectionParticipant: {skip: "internal command"},
_shardsvrDropCollection: {skip: "internal command"},
_shardsvrDropCollectionIfUUIDNotMatchingWithWriteConcern: {skip: "internal command"},
_shardsvrDropCollectionParticipant: {skip: "internal command"},
_shardsvrDropIndexes: {skip: "internal command"},
_shardsvrDropIndexesParticipant: {skip: "internal command"},
_shardsvrDrainOngoingDDLOperations: {skip: "internal command"},
_shardsvrDropDatabase: {skip: "internal command"},
_shardsvrDropDatabaseParticipant: {skip: "internal command"},
_shardsvrEndMigrationBlockingOperation: {skip: "internal command"},
_shardsvrFetchCollMetadata: {skip: "internal command"},
_shardsvrGetStatsForBalancing: {skip: "internal command"},
_shardsvrCheckCanConnectToConfigServer: {skip: "internal command"},
_shardsvrJoinDDLCoordinators: {skip: "internal command"},
_shardsvrJoinMigrations: {skip: "internal command"},
_shardsvrMergeAllChunksOnShard: {skip: "internal command"},
_shardsvrMergeChunks: {skip: "internal command"},
_shardsvrMovePrimary: {skip: "internal command"},
_shardsvrMovePrimaryEnterCriticalSection: {skip: "internal command"},
_shardsvrMovePrimaryExitCriticalSection: {skip: "internal command"},
_shardsvrMoveRange: {skip: "internal command"},
_shardsvrNotifyShardingEvent: {skip: "internal command"},
_shardsvrRefineCollectionShardKey: {skip: "internal command"},
_shardsvrRenameCollection: {skip: "internal command"},
_shardsvrRenameCollectionParticipant: {skip: "internal command"},
_shardsvrRenameCollectionParticipantUnblock: {skip: "internal command"},
_shardsvrRenameIndexMetadata: {skip: "internal command"},
_shardsvrReshardCollection: {skip: "internal command"},
_shardsvrReshardingDonorFetchFinalCollectionStats: {skip: "internal command"},
_shardsvrReshardingDonorStartChangeStreamsMonitor: {skip: "internal command"},
_shardsvrReshardingOperationTime: {skip: "internal command"},
_shardsvrReshardRecipientClone: {skip: "internal command"},
_shardsvrResolveView: {skip: "internal command"},
_shardsvrRunSearchIndexCommand: {skip: "internal command"},
_shardsvrSetAllowMigrations: {skip: "internal command"},
_shardsvrSetClusterParameter: {skip: "internal command"},
_shardsvrSetUserWriteBlockMode: {skip: "internal command"},
_shardsvrValidateShardKeyCandidate: {skip: "internal command"},
_shardsvrCollMod: {skip: "internal command"},
_shardsvrCollModParticipant: {skip: "internal command"},
_shardsvrConvertToCappedParticipant: {skip: "internal command"},
_shardsvrParticipantBlock: {skip: "internal command"},
_shardsvrUntrackUnsplittableCollection: {skip: "internal command"},
streams_startStreamProcessor: {skip: "internal command"},
streams_startStreamSample: {skip: "internal command"},
streams_stopStreamProcessor: {skip: "internal command"},
streams_listStreamProcessors: {skip: "internal command"},
streams_getMoreStreamSample: {skip: "internal command"},
streams_getStats: {skip: "internal command"},
streams_testOnlyInsert: {skip: "internal command"},
streams_getMetrics: {skip: "internal command"},
streams_updateFeatureFlags: {skip: "internal command"},
streams_testOnlyGetFeatureFlags: {skip: "internal command"},
streams_writeCheckpoint: {skip: "internal command"},
streams_sendEvent: {skip: "internal command"},
streams_updateConnection: {skip: "internal command"},
_transferMods: {skip: "internal command"},
abortMoveCollection: {skip: "does not accept write concern"},
abortReshardCollection: {skip: "does not accept write concern"},
abortRewriteCollection: {skip: "does not accept write concern"},
abortTransaction: {skip: "not supported on timeseries views"},
abortUnshardCollection: {skip: "does not accept write concern"},
addShard: {skip: "unrelated"},
addShardToZone: {skip: "does not accept write concern"},
aggregate: {
noop: {
// The pipeline will not match any docs, so nothing should be written to "out"
req: {
aggregate: collName,
pipeline: [{$match: {"meta.x": 1}}, {$out: "out"}],
cursor: {},
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
assert.commandWorked(coll.deleteMany({meta: {x: 1, y: 1}}));
assert.eq(coll.count({meta: {x: 1, y: 1}}), 0);
assert.commandWorked(coll.getDB().createCollection("out"));
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
}
assert.eq(coll.getDB().out.find().itcount(), 0);
coll.getDB().out.drop();
},
},
success: {
// The pipeline should write a single doc to "out"
req: {
aggregate: collName,
pipeline: [{$match: {"meta.x": 1}}, {$out: "out"}],
cursor: {},
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
assert.commandWorked(coll.getDB().createCollection("out"));
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().out.find().itcount(), 0);
} else {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().out.find().itcount(), 1);
}
assert.eq(coll.count({meta: {x: 1, y: 1}}), 1);
coll.getDB().out.drop();
},
},
failure: {
// Attempt to update non meta field
req: {
aggregate: collName,
pipeline: [{$match: {"meta.x": 1}}, {$set: {time: "deadbeef"}}, {$out: "out"}],
cursor: {},
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.insert({meta: 2, time: timeValue}));
assert.commandWorked(
coll.getDB().createCollection("out", {timeseries: {timeField: "time", metaField: "meta"}}),
);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().out.find().itcount(), 0);
coll.getDB().out.drop();
},
},
},
// TODO SPM-2513 Define test case for analyze once the command is user facing
analyze: {skip: "internal only"},
analyzeShardKey: {skip: "does not accept write concern"},
appendOplogNote: {
success: {
// appendOplogNote basic
req: {appendOplogNote: 1, data: {meta: 1}},
setupFunc: (coll) => {},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
},
admin: true,
},
},
applyOps: {
noop: {
// 'applyOps' where the update is a no-op
req: {applyOps: [{op: "u", ns: fullNs, o: {meta: 1, _id: 0}, o2: {meta: 1}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
},
confirmFunc: (res, coll, cluster, clusterType) => {
if (clusterType == "sharded") {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.results[0], true);
} else {
assert.commandFailedWithCode(res, ErrorCodes.CommandNotSupportedOnView);
}
assert.eq(res.applied, 1);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({meta: 1}), 1);
},
},
success: {
// 'applyOps' basic insert
req: {applyOps: [{op: "i", ns: fullNs, o: {meta: 2, time: timeValue}}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
},
confirmFunc: (res, coll, cluster, clusterType) => {
if (clusterType == "sharded") {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.results[0], true);
assert.eq(coll.find().itcount(), 2);
assert.eq(coll.count({time: timeValue}), 2);
} else {
assert.commandFailedWithCode(res, ErrorCodes.CommandNotSupportedOnView);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({time: timeValue}), 1);
}
assert.eq(res.applied, 1);
},
},
failure: {
// 'applyOps' attempt to update to bad value
req: {
applyOps: [{op: "u", ns: fullNs, o: {time: timeValue, _id: 0}, o2: {time: "deadbeef"}}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue, _id: 0}));
},
confirmFunc: (res, coll) => {
assert.eq(res.applied, 1);
assert.eq(res.ok, 0);
assert.eq(res.results[0], false);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({meta: 1}), 1);
},
},
},
authenticate: {skip: "does not accept write concern"},
autoCompact: {skip: "does not accept write concern"},
autoSplitVector: {skip: "does not accept write concern"},
balancerCollectionStatus: {skip: "does not accept write concern"},
balancerStart: {skip: "does not accept write concern"},
balancerStatus: {skip: "does not accept write concern"},
balancerStop: {skip: "does not accept write concern"},
buildInfo: {skip: "does not accept write concern"},
bulkWrite: {
noop: {
// The doc to update doesn't exist
req: {
bulkWrite: 1,
ops: [
{
update: 0,
multi: true,
filter: {"meta.x": 0},
updateMods: {$set: {"meta.y": 1}},
},
],
nsInfo: [{ns: fullNs}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 0);
},
admin: true,
},
success: {
// Basic insert in a bulk write
req: {
bulkWrite: 1,
ops: [{insert: 0, document: {meta: 2, time: timeValue}}],
nsInfo: [{ns: fullNs}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: ISODate()}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nErrors, 0);
assert.eq(res.nInserted, 1);
},
admin: true,
},
failure: {
// Attempt to update doc with bad time value
req: {
bulkWrite: 1,
ops: [
{
update: 0,
filter: {"meta.x": 1},
multi: true,
updateMods: {$set: {time: "deadbeef"}},
},
],
nsInfo: [{ns: fullNs}],
},
setupFunc: (coll, cluster) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: ISODate()}));
},
confirmFunc: (res, coll, cluster) => {
assert.eq(res.nErrors, 1);
assert(res.cursor && res.cursor.firstBatch && res.cursor.firstBatch.length == 1);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[0].code);
},
admin: true,
},
},
changePrimary: {
noop: {
// The destination shard is already the primary shard for the db
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[0]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Basic change primary
req: (cluster) => ({changePrimary: dbName, to: getShardNames(cluster)[1]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard1.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
// Change the primary back
assert.commandWorked(coll.getDB().adminCommand({changePrimary: dbName, to: cluster.shard0.shardName}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
},
admin: true,
},
},
checkMetadataConsistency: {skip: "does not accept write concern"},
checkShardingIndex: {skip: "does not accept write concern"},
cleanupOrphaned: {skip: "only exist on direct shard connection"},
cleanupReshardCollection: {skip: "does not accept write concern"},
cleanupStructuredEncryptionData: {skip: "does not accept write concern"},
clearJumboFlag: {skip: "does not accept write concern"},
clearLog: {skip: "does not accept write concern"},
cloneCollectionAsCapped: {skip: "not supported on timeseries views"},
clusterAbortTransaction: {skip: "already tested by 'abortTransaction' tests on mongos"},
clusterAggregate: {skip: "already tested by 'aggregate' tests on mongos"},
clusterBulkWrite: {skip: "already tested by 'bulkWrite' tests on mongos"},
clusterCommitTransaction: {skip: "already tested by 'commitTransaction' tests on mongos"},
clusterCount: {skip: "already tested by 'count' tests on mongos"},
clusterDelete: {skip: "already tested by 'delete' tests on mongos"},
clusterFind: {skip: "already tested by 'find' tests on mongos"},
clusterGetMore: {skip: "already tested by 'getMore' tests on mongos"},
clusterInsert: {skip: "already tested by 'insert' tests on mongos"},
clusterUpdate: {skip: "already tested by 'update' tests on mongos"},
collMod: {
noop: {
// Set expireAfterSeconds off for already non expiring
req: {collMod: collName, expireAfterSeconds: "off"},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Add validator
req: {collMod: collName, index: {keyPattern: {a: 1}, hidden: true}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
}),
);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getIndexes().length, 2);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
failure: {
// Index to be updated does not exist
req: {collMod: collName, index: {name: "a_1", hidden: true}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
}),
);
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({dropIndexes: collName, index: "a_1"}),
);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.IndexNotFound);
assert.eq(coll.getDB().getCollectionInfos({name: collName})[0].options.validator, undefined);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
collStats: {skip: "does not accept write concern"},
commitReshardCollection: {skip: "does not accept write concern"},
commitShardRemoval: {skip: "unrelated"},
commitTransaction: {skip: "not supported on timeseries views"},
commitTransitionToDedicatedConfigServer: {skip: "unrelated"},
compact: {skip: "does not accept write concern"},
compactStructuredEncryptionData: {skip: "does not accept write concern"},
configureCollectionBalancing: {skip: "does not accept write concern"},
configureFailPoint: {skip: "internal command"},
configureQueryAnalyzer: {skip: "does not accept write concern"},
connPoolStats: {skip: "does not accept write concern"},
connPoolSync: {skip: "internal command"},
connectionStatus: {skip: "does not accept write concern"},
convertToCapped: {skip: "not supported on timeseries views"},
coordinateCommitTransaction: {skip: "internal command"},
count: {skip: "does not accept write concern"},
cpuload: {skip: "does not accept write concern"},
create: {
noop: {
// Coll already exists
req: {create: collName, timeseries: {timeField: "time", metaField: "meta"}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
coll.insert({meta: 1, time: ISODate()});
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getCollectionInfos({name: collName}).length, 1);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic create coll
req: {create: collName, timeseries: {timeField: "time", metaField: "meta"}},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert(coll.drop());
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
failure: {
// Attempt to create a view and output to a nonexistent collection
req: {create: "viewWithOut", viewOn: collName, pipeline: [{$out: "nonexistentColl"}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.eq(coll.find().itcount(), 1);
assert.commandWorked(coll.getDB().runCommand({drop: "nonexistentColl"}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
if (clusterType == "sharded") {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
} else {
assert.commandFailedWithCode(res, ErrorCodes.OptionNotSupportedOnView);
}
assert.eq(coll.find().itcount(), 1);
assert(!coll.getDB().getCollectionNames().includes("nonexistentColl"));
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
createIndexes: {
noop: {
// Index already exists
req: {
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {a: 1}, name: "a_1"}],
commitQuorum: "majority",
}),
);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let details = res;
if ("raw" in details) {
const raw = details.raw;
details = raw[Object.keys(raw)[0]];
}
assert.eq(details.numIndexesBefore, details.numIndexesAfter);
assert.eq(details.note, "all indexes already exist");
},
},
success: {
// Basic create index
req: {
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let details = res;
if ("raw" in details) {
const raw = details.raw;
details = raw[Object.keys(raw)[0]];
}
assert.eq(details.numIndexesBefore, details.numIndexesAfter - 1);
},
},
failure: {
// Attempt to create two indexes with the same name and different keys
req: {
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {b: -1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.IndexKeySpecsConflict);
},
},
},
createRole: wcCommandsTests["createRole"],
createSearchIndexes: {skip: "does not accept write concern"},
createUnsplittableCollection: {skip: "internal command"},
createUser: wcCommandsTests["createUser"],
currentOp: {skip: "does not accept write concern"},
dataSize: {skip: "does not accept write concern"},
dbCheck: {skip: "does not accept write concern"},
dbHash: {skip: "does not accept write concern"},
dbStats: {skip: "does not accept write concern"},
delete: {
noop: {
// The query will not match any doc
req: {delete: collName, deletes: [{q: {"meta.x": 1}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
assert.commandWorked(coll.remove({meta: {x: 1, y: 1}, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(coll.count({meta: {x: 1, y: 1}}), 0);
},
},
success: {
req: {delete: collName, deletes: [{q: {"meta.x": 1}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 1);
assert.eq(coll.find().itcount(), 0);
},
},
},
distinct: {skip: "does not accept write concern"},
drop: {
noop: {
// The collection has already been dropped
req: {drop: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.getDB().runCommand({drop: collName}));
assert.eq(coll.find().itcount(), 0);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic drop collection
req: {drop: collName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 0);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropAllRolesFromDatabase: wcCommandsTests["dropAllRolesFromDatabase"],
dropAllUsersFromDatabase: wcCommandsTests["dropAllUsersFromDatabase"],
dropConnections: {skip: "does not accept write concern"},
dropDatabase: {
noop: {
// Database has already been dropped
req: {dropDatabase: 1},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorkedIgnoringWriteConcernErrors(coll.getDB().runCommand({dropDatabase: 1}));
assert.eq(coll.find().itcount(), 0);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
},
},
success: {
// Basic drop database
req: {dropDatabase: 1},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropIndexes: {
noop: {
// Passing "*" will drop all indexes except the index created for shard key in pre
// setup.
// The only index on this collection is that one so the command will be a no-op in
// sharded cluster. In replica sets, there is a default index created on the timeseries
// fields - this will be dropped in setup.
req: {
dropIndexes: collName,
index: "*",
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: "a", time: timeValue}));
assert.commandWorked(coll.getDB().runCommand({dropIndexes: collName, index: "*"}));
optionalArgs.numIndexesBefore = coll.getIndexes().length;
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
// Make a non-acknowledged write so that the no-op will have to fail with WCE
assert.commandWorkedIgnoringWriteConcernErrors(
coll.insert({meta: "a", time: timeValue}, {writeConcern: {w: "majority", wtimeout: 100}}),
);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
success: {
// Basic drop index
req: {
dropIndexes: collName,
index: "b_1",
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: "b", time: timeValue}));
const numIndexesBefore = coll.getIndexes().length;
optionalArgs.numIndexesBefore = numIndexesBefore;
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [{key: {b: 1}, name: "b_1"}],
commitQuorum: "majority",
}),
);
assert.eq(coll.getIndexes().length, numIndexesBefore + 1);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getIndexes().length, optionalArgs.numIndexesBefore);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
},
},
dropRole: wcCommandsTests["dropRole"],
dropSearchIndex: {skip: "does not accept write concern"},
dropUser: wcCommandsTests["dropUser"],
echo: {skip: "does not accept write concern"},
enableSharding: {
targetConfigServer: true,
noop: {
// Sharding already enabled
req: {enableSharding: dbName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
assert.commandWorked(coll.getDB().adminCommand({enableSharding: dbName}));
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding
// no longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 1);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
success: {
// Basic enable sharding
req: {enableSharding: dbName},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.getDB().runCommand({dropDatabase: 1}));
assert.eq(coll.getDB().getSiblingDB("config").databases.find({_id: dbName}).itcount(), 0);
// TODO SERVER-97754 Do not stop the remaining secondary once enableSharding
// no longer override user provided writeConcern
secondariesRunning[0].getDB("admin").fsyncLock();
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
secondariesRunning[0].getDB("admin").fsyncUnlock();
},
admin: true,
},
},
endSessions: {skip: "does not accept write concern"},
eseRotateActiveKEK: {skip: "does not accept write concern"},
explain: {skip: "does not accept write concern"},
features: {skip: "does not accept write concern"},
filemd5: {skip: "does not accept write concern"},
find: {skip: "does not accept write concern"},
findAndModify: {
// noop and success cases are not applicable because findAndModify does single
// updates and non-multi updates are not allowed on timeseries views
failure: {
req: {findAndModify: collName, query: {"meta.x": 1}, update: {time: "deadbeef"}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
},
confirmFunc: (res, coll, cluster) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.includes([ErrorCodes.InvalidOptions, ErrorCodes.BadValue], res.code);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({meta: {x: 1, y: 1}}), 1);
},
},
},
flushRouterConfig: {skip: "does not accept write concern"},
forceerror: {skip: "test command"},
fsync: {skip: "does not accept write concern"},
fsyncUnlock: {skip: "does not accept write concern"},
getAuditConfig: {skip: "does not accept write concern"},
getChangeStreamState: {skip: "removed in v8.3"},
getClusterParameter: {skip: "does not accept write concern"},
getCmdLineOpts: {skip: "does not accept write concern"},
getDatabaseVersion: {skip: "internal command"},
getDefaultRWConcern: {skip: "does not accept write concern"},
getDiagnosticData: {skip: "does not accept write concern"},
getESERotateActiveKEKStatus: {skip: "does not accept write concern"},
getLog: {skip: "does not accept write concern"},
getMore: {skip: "does not accept write concern"},
getParameter: {skip: "does not accept write concern"},
getQueryableEncryptionCountInfo: {skip: "does not accept write concern"},
getShardMap: {skip: "internal command"},
getShardVersion: {skip: "internal command"},
getTrafficRecordingStatus: {skip: "does not accept write concern"},
getTransitionToDedicatedConfigServerStatus: {skip: "unrelated"},
godinsert: {skip: "for testing only"},
grantPrivilegesToRole: wcCommandsTests["grantPrivilegesToRole"],
grantRolesToRole: wcCommandsTests["grantRolesToRole"],
grantRolesToUser: wcCommandsTests["grantRolesToUser"],
handshake: {skip: "does not accept write concern"},
hello: {skip: "does not accept write concern"},
hostInfo: {skip: "does not accept write concern"},
httpClientRequest: {skip: "does not accept write concern"},
exportCollection: {skip: "internal command"},
importCollection: {skip: "internal command"},
insert: {
// A no-op insert that returns success is not possible
success: {
// Basic insert
req: {insert: collName, documents: [{meta: 11, time: timeValue}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 11, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 1);
assert.eq(coll.find().itcount(), 2);
},
},
},
internalRenameIfOptionsAndIndexesMatch: {skip: "internal command"},
invalidateUserCache: {skip: "does not accept write concern"},
isdbgrid: {skip: "does not accept write concern"},
isMaster: {skip: "does not accept write concern"},
killAllSessions: {skip: "does not accept write concern"},
killAllSessionsByPattern: {skip: "does not accept write concern"},
killCursors: {skip: "does not accept write concern"},
killOp: {skip: "does not accept write concern"},
killSessions: {skip: "does not accept write concern"},
listCollections: {skip: "does not accept write concern"},
listCommands: {skip: "does not accept write concern"},
listDatabases: {skip: "does not accept write concern"},
listDatabasesForAllTenants: {skip: "does not accept write concern"},
listIndexes: {skip: "does not accept write concern"},
listSearchIndexes: {skip: "does not accept write concern"},
listShards: {skip: "does not accept write concern"},
lockInfo: {skip: "does not accept write concern"},
logApplicationMessage: {skip: "does not accept write concern"},
logMessage: {skip: "does not accept write concern"},
logRotate: {skip: "does not accept write concern"},
logout: {skip: "does not accept write concern"},
makeSnapshot: {skip: "does not accept write concern"},
mapReduce: {skip: "deprecated"},
mergeAllChunksOnShard: {skip: "does not accept write concern"},
mergeChunks: {skip: "does not accept write concern"},
moveChunk: {skip: "not applicable on timeseries views"},
moveCollection: {skip: "does not accept write concern"},
movePrimary: {
noop: {
// The destination shard is already the primary shard for the db
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[0]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Basic movePrimary
req: (cluster) => ({movePrimary: dbName, to: getShardNames(cluster)[1]}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: getShardNames(cluster)[0]}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
// Change the primary back
assert.commandWorked(coll.getDB().adminCommand({movePrimary: dbName, to: cluster.shard0.shardName}));
assert.eq(coll.getDB().getDatabasePrimaryShardId(), cluster.shard0.shardName);
},
admin: true,
},
},
moveRange: {skip: "not applicable on timeseries views"},
multicast: {skip: "does not accept write concern"},
netstat: {skip: "internal command"},
oidcListKeys: {skip: "does not accept write concern"},
oidcRefreshKeys: {skip: "does not accept write concern"},
pinHistoryReplicated: {skip: "internal command"},
ping: {skip: "does not accept write concern"},
planCacheClear: {skip: "does not accept write concern"},
planCacheClearFilters: {skip: "does not accept write concern"},
planCacheListFilters: {skip: "does not accept write concern"},
planCacheSetFilter: {skip: "does not accept write concern"},
prepareTransaction: {skip: "internal command"},
profile: {skip: "does not accept write concern"},
reIndex: {skip: "does not accept write concern"},
reapLogicalSessionCacheNow: {skip: "does not accept write concern"},
refineCollectionShardKey: {
noop: {
// Refine to same shard key
req: (cluster, coll) => ({refineCollectionShardKey: fullNs, key: getShardKey(coll, fullNs)}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
admin: true,
},
success: {
// Add additional field to shard key
req: (cluster, coll) => ({
refineCollectionShardKey: fullNs,
key: Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
}),
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
let sk = getShardKey(coll, fullNs);
optionalArgs.origSk = sk;
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), sk), 0);
assert.commandWorked(
coll.getDB().runCommand({
createIndexes: collName,
indexes: [
{
key: Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
name: "sk_1",
},
],
commitQuorum: "majority",
}),
);
stopAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
},
confirmFunc: (res, coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(
bsonWoCompare(
getShardKey(coll, fullNs),
Object.assign({}, getShardKey(coll, fullNs), {"meta.a": 1}),
),
0,
);
restartAdditionalSecondariesIfSharded(clusterType, cluster, secondariesRunning);
assert.commandWorked(
coll.getDB().adminCommand({refineCollectionShardKey: fullNs, key: optionalArgs.sk}),
);
assert.eq(bsonWoCompare(getShardKey(coll, fullNs), optionalArgs.sk), 0);
},
admin: true,
},
},
refreshLogicalSessionCacheNow: {skip: "does not accept write concern"},
refreshSessions: {skip: "does not accept write concern"},
releaseMemory: {skip: "does not accept write concern"},
removeShard: {skip: "unrelated"},
removeShardFromZone: {skip: "does not accept write concern"},
renameCollection: {skip: "not supported on timeseries views"},
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
replSetFreeze: {skip: "does not accept write concern"},
replSetGetConfig: {skip: "does not accept write concern"},
replSetGetRBID: {skip: "does not accept write concern"},
replSetGetStatus: {skip: "does not accept write concern"},
replSetHeartbeat: {skip: "does not accept write concern"},
replSetInitiate: {skip: "does not accept write concern"},
replSetMaintenance: {skip: "does not accept write concern"},
replSetReconfig: {skip: "does not accept write concern"},
replSetRequestVotes: {skip: "does not accept write concern"},
replSetResizeOplog: {skip: "does not accept write concern"},
replSetStepDown: {skip: "does not accept write concern"},
replSetStepUp: {skip: "does not accept write concern"},
replSetSyncFrom: {skip: "does not accept write concern"},
replSetTest: {skip: "does not accept write concern"},
replSetTestEgress: {skip: "does not accept write concern"},
replSetUpdatePosition: {skip: "does not accept write concern"},
resetPlacementHistory: {skip: "internal command"},
reshardCollection: {skip: "does not accept write concern"},
revokePrivilegesFromRole: wcCommandsTests["revokePrivilegesFromRole"],
revokeRolesFromRole: wcCommandsTests["revokeRolesFromRole"],
revokeRolesFromUser: wcCommandsTests["revokeRolesFromUser"],
rewriteCollection: {skip: "does not accept write concern"},
rolesInfo: {skip: "does not accept write concern"},
rotateCertificates: {skip: "does not accept write concern"},
rotateFTDC: {skip: "does not accept write concern"},
saslContinue: {skip: "does not accept write concern"},
saslStart: {skip: "does not accept write concern"},
sbe: {skip: "internal command"},
serverStatus: {skip: "does not accept write concern"},
setAllowMigrations: wcCommandsTests["setAllowMigrations"],
setAuditConfig: {skip: "does not accept write concern"},
setCommittedSnapshot: {skip: "internal command"},
setDefaultRWConcern: wcCommandsTests["setDefaultRWConcern"],
setFeatureCompatibilityVersion: wcCommandsTests["setFeatureCompatibilityVersion"],
setProfilingFilterGlobally: {skip: "does not accept write concern"},
setIndexCommitQuorum: {skip: "not supported on timeseries views"},
setParameter: {skip: "does not accept write concern"},
setShardVersion: {skip: "internal command"},
setChangeStreamState: {skip: "removed in v8.3"},
setClusterParameter: {skip: "does not accept write concern"},
setQuerySettings: {skip: "does not accept write concern"},
removeQuerySettings: {skip: "does not accept write concern"},
setUserWriteBlockMode: {skip: "does not accept write concern"},
shardCollection: wcCommandsTests["shardCollection"],
shardDrainingStatus: {skip: "unrelated"},
shardingState: {skip: "does not accept write concern"},
shutdown: {skip: "does not accept write concern"},
sleep: {skip: "does not accept write concern"},
split: {skip: "does not accept write concern"},
splitChunk: {skip: "does not accept write concern"},
splitVector: {skip: "internal command"},
stageDebug: {skip: "does not accept write concern"},
startSession: {skip: "does not accept write concern"},
startShardDraining: {skip: "unrelated"},
stopShardDraining: {skip: "unrelated"},
startTrafficRecording: {skip: "does not accept write concern"},
startTransitionToDedicatedConfigServer: {skip: "unrelated"},
stopTrafficRecording: {skip: "does not accept write concern"},
stopTransitionToDedicatedConfigServer: {skip: "unrelated"},
sysprofile: {skip: "internal command"},
testCommandFeatureFlaggedOnLatestFCV83: {skip: "internal command"},
testDeprecation: {skip: "test command"},
testDeprecationInVersion2: {skip: "test command"},
testInternalTransactions: {skip: "internal command"},
testRemoval: {skip: "test command"},
testReshardCloneCollection: {skip: "internal command"},
testVersions1And2: {skip: "test command"},
testVersion2: {skip: "test command"},
timeseriesCatalogBucketParamsChanged: {skip: "internal command"},
top: {skip: "does not accept write concern"},
transitionFromDedicatedConfigServer: {skip: "unrelated"},
transitionToDedicatedConfigServer: {skip: "unrelated"},
transitionToShardedCluster: {skip: "internal command"},
unshardCollection: {skip: "does not accept write concern"},
untrackUnshardedCollection: {skip: "does not accept write concern"},
update: {
noop: {
// The query will not match any doc
req: {
update: collName,
updates: [{q: {"meta.x": 0}, u: {$set: {"meta.y": 1}}, multi: true}],
},
setupFunc: (coll) => {},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
assert.eq(coll.find().itcount(), 0);
},
},
success: {
// Basic update
req: {
update: collName,
updates: [{q: {"meta.x": 1}, u: {$set: {"meta.y": 2}}, multi: true}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert([{meta: {x: 1, y: 1}, time: timeValue}]));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find({"meta.y": 2}).toArray().length, 1);
},
},
failure: {
req: {
update: collName,
updates: [{q: {"meta.x": 1}, u: {$set: {"meta.y": 2, time: "deadbeef"}}, multi: true}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1, y: 1}, time: timeValue}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.includes([ErrorCodes.InvalidOptions, ErrorCodes.BadValue], res.writeErrors[0]["code"]);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
assert.eq(coll.count({"meta.y": 1}), 1);
},
},
},
updateRole: wcCommandsTests["updateRole"],
updateSearchIndex: {skip: "does not accept write concern"},
updateUser: wcCommandsTests["updateRole"],
updateZoneKeyRange: {skip: "does not accept write concern"},
usersInfo: {skip: "does not accept write concern"},
validate: {skip: "does not accept write concern"},
validateDBMetadata: {skip: "does not accept write concern"},
voteAbortIndexBuild: {skip: "internal command"},
voteCommitImportCollection: {skip: "internal command"},
voteCommitIndexBuild: {skip: "internal command"},
waitForFailPoint: {skip: "test command"},
getShardingReady: {skip: "internal command"},
whatsmysni: {skip: "does not accept write concern"},
whatsmyuri: {skip: "internal command"},
};
// A list of additional CRUD ops which exercise different write paths, and do error handling
// differently than the basic write path exercised in wcCommandsTestsT.
const additionalCRUDOpsTimeseriesViews = {
"deleteMany": {
noop: {
req: {delete: collName, deletes: [{q: {"meta.x": {$lt: 0}}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
assert.commandWorked(coll.insert({meta: {x: 2}, time: timeValue}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(coll.find().itcount(), 2);
},
},
success: {
req: {delete: collName, deletes: [{q: {"meta.x": {$gt: -5}}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
assert.commandWorked(coll.insert({meta: {x: 2}, time: timeValue}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 2);
assert.eq(coll.find().itcount(), 0);
},
},
},
"insertMany": {
success: {
req: {
insert: collName,
documents: [
{meta: -2, time: timeValue},
{meta: 2, time: timeValue},
],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: 1, time: timeValue}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 2);
assert.eq(coll.find().itcount(), 3);
},
},
failure: {
req: {insert: collName, documents: [{meta: -2, time: timeValue}, {meta: 2}]},
setupFunc: (coll) => {},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.includes([5743702, ErrorCodes.BadValue], res.writeErrors[0].code);
assert.eq(res.n, 1);
assert.eq(coll.find().itcount(), 1);
},
},
},
"updateMany": {
noop: {
req: {
update: collName,
updates: [{q: {"meta.x": {$gt: -21}}, u: {$set: {"meta.y": 1}}, multi: true}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.commandWorked(coll.remove({"meta.x": {$gt: -21}}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 0);
assert.eq(res.n, 0);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
},
},
success: {
req: {
update: collName,
updates: [{q: {"meta.x": {$gt: -21}}, u: {$set: {"meta.y": 1}}, multi: true}],
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 3);
assert.eq(res.n, 3);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
},
},
failure: {
req: {
update: collName,
updates: [{q: {"meta.x": {$gt: -5}}, u: {$set: {time: "deadbeef"}}, multi: true}],
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: ISODate()}));
assert.commandWorked(coll.insert({meta: {x: 2}, time: ISODate()}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
},
},
},
"findOneAndRemove": {
noop: {
req: {findAndModify: collName, query: {"meta.x": 1}, remove: true},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
assert.commandWorked(coll.remove({meta: {x: 1}, time: timeValue}));
assert.eq(coll.find().itcount(), 0);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
}
assert.eq(res.value, null);
assert.eq(coll.find().itcount(), 0);
},
},
success: {
req: {findAndModify: collName, query: {"meta.x": 1}, remove: true},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.value.meta.x, 1);
assert.eq(coll.find().itcount(), 0);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find().itcount(), 1);
}
},
},
},
"findOneAndUpdate": {
// Modifier updates
// No noop and success cases as we cannot perform a non-multi update on a
// time-series
// collection. findAndModify does not support multi updates.
failure: {
req: {
findAndModify: collName,
query: {"meta.x": 1},
update: {time: "deadbeef"},
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({meta: {x: 1}, time: timeValue}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.code);
},
},
},
"unorderedBatch": {
noop: {
// The two writes would execute the same update, so one will be a no-op.
req: {
update: collName,
updates: [
{q: {"meta.x": 21}, u: {$set: {"meta.y": 21}}, multi: true},
{q: {"meta.x": {$gte: 20}}, u: {$set: {"meta.y": 21}}, multi: true},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22, y: 21}, time: ISODate()},
{meta: {x: -20, y: 21}, time: ISODate()},
{meta: {x: 20, y: 21}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.nModified, 1);
assert.eq(coll.find({"meta.y": 21}).toArray().length, 4);
},
},
success: {
// All updates should succeed, but all should return WCEs.
req: {
update: collName,
updates: [
{q: {"meta.x": -20}, u: {$set: {"meta.y": 1}}, multi: true},
{q: {"meta.x": 20}, u: {$set: {"meta.y": 1}}, multi: true},
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22, y: 21}, time: ISODate()},
{meta: {x: -20, y: 21}, time: ISODate()},
{meta: {x: 20, y: 21}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 3);
assert.eq(res.nModified, 3);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
} else {
// The two phase write path returns WriteConcernTimeout in the write
// errors array.
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 3);
res.writeErrors.forEach((err) => {
assert.eq(err.code, ErrorCodes.WriteConcernTimeout);
});
assert.eq(res.nModified, 0);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
}
},
},
failure: {
// The second update should fail. This is an unordered batch, so the
// other two updates should succeed, but still return WCEs.
req: {
update: collName,
updates: [
{q: {"meta.x": -20}, u: {$set: {"meta.y": 3}}, multi: true},
{q: {"meta.x": 20}, u: {$set: {time: "deadbeef"}}, multi: true},
{q: {"meta.x": 21}, u: {$set: {"meta.y": 3}}, multi: true},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
assert.eq(res.nModified, 2);
assert.eq(coll.find().itcount(), 4);
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 2);
} else {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 3);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[1].code);
assert.eq(res.writeErrors[2].code, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 0);
}
},
},
},
"orderedBatch": {
noop: {
// The last update is a no-op.
req: {
update: collName,
updates: [
{q: {"meta.x": {$gte: -21}}, u: {$set: {"meta.y": 1}}, multi: true},
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.nModified, 3);
assert.eq(coll.find({"meta.y": 1}).itcount(), 3);
},
},
success: {
// All updates should succeed, but all should return WCEs.
req: {
update: collName,
updates: [
{q: {"meta.x": -20}, u: {$set: {"meta.y": 1}}, multi: true},
{q: {"meta.x": 20}, u: {$set: {"meta.y": 1}}, multi: true},
{q: {"meta.x": 21}, u: {$set: {"meta.y": 1}}, multi: true},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 3);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 3);
} else {
// We stop execution after the first write because it fails with a WCE
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nModified, 0);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
}
},
},
failure: {
// The second update should fail. This is an ordered batch, so the
// last update should not be executed. The first and second should still return
// WCEs.
req: {
update: collName,
updates: [
{q: {"meta.x": -20}, u: {$set: {"meta.y": 3}}, multi: true},
{q: {"meta.x": 20}, u: {$set: {time: "deadbeef"}}, multi: true},
{q: {"meta.x": 21}, u: {$set: {"meta.y": 3}}, multi: true},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
{meta: {x: 21}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.writeErrors[0].code);
assert.eq(res.nModified, 1);
assert.eq(coll.find().itcount(), 4);
assert.eq(coll.find({"meta.y": {$exists: true}}).toArray().length, 1);
} else {
// We stop execution after the first write because it fails with a WCE
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nModified, 0);
assert.eq(coll.find({"meta.y": 1}).toArray().length, 0);
}
},
},
},
"bulkWriteUnordered": {
// The two writes would execute the same delete, so one will be a no-op.
noop: {
req: {
bulkWrite: 1,
ops: [
{delete: 0, filter: {"meta.x": {$gte: -20}}, multi: true},
{delete: 0, filter: {"meta.x": -20}, multi: true},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
}
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.nDeleted, 1);
assert.eq(coll.find().itcount(), 1);
},
admin: true,
},
success: {
req: {
bulkWrite: 1,
ops: [
{insert: 0, document: {meta: 22, time: timeValue}},
{
update: 0,
filter: {"meta.x": -20},
updateMods: {$set: {"meta.y": 2}},
multi: true,
},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 1);
assert.eq(coll.find({"meta.y": 2}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 0);
assert.eq(coll.find({"meta.y": 2}).itcount(), 0);
}
assert.eq(res.nInserted, 1);
assert.eq(coll.find().itcount(), 4);
},
admin: true,
},
failure: {
// The second update should fail, but this is an unordered batch so the other 2
// updates should succeed.
req: {
bulkWrite: 1,
ops: [
{
update: 0,
filter: {"meta.x": {$gte: -20}},
updateMods: {$set: {"meta.y": 5}},
multi: true,
},
{
update: 0,
filter: {"meta.x": -22},
updateMods: {$set: {time: "deadbeef"}},
multi: true,
},
{
update: 0,
filter: {"meta.x": -22},
updateMods: {$set: {"meta.y": 4}},
multi: true,
},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 3);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 2);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[1].code);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[2].ok, 1);
assert.eq(res.cursor.firstBatch[2].n, 1);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 3);
assert.eq(coll.find({"meta.y": 4}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[2].ok, 0);
assert.eq(res.cursor.firstBatch[2].n, 0);
assert.eq(res.cursor.firstBatch[2].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 2);
assert.eq(res.nModified, 2);
assert.eq(coll.find({"meta.y": 4}).itcount(), 1);
}
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({"meta.y": 5}).itcount(), 2);
},
admin: true,
},
},
"bulkWriteOrdered": {
// The two deletes would remove the same doc, so one will be a no-op.
noop: {
req: {
bulkWrite: 1,
ops: [
{delete: 0, filter: {"meta.x": {$gte: -20}}, multi: true},
{delete: 1, filter: {"meta.x": -20}},
{insert: 0, document: {meta: 25, time: timeValue}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch.length, 3);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.cursor.firstBatch[2].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(coll.find().itcount(), 2);
} else {
// The write without shard key will execute a transaction and fail the
// write with WriteConcernTimeout, so the insert will not execute.
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(coll.find().itcount(), 1);
}
},
admin: true,
},
success: {
req: {
bulkWrite: 1,
ops: [
{
update: 0,
filter: {"meta.x": {$gte: -10}},
updateMods: {$set: {"meta.y": 1}},
multi: true,
},
{
update: 1,
filter: {"meta.x": -20},
updateMods: {$set: {"meta.y": 2}},
multi: true,
},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"meta.x": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 2);
assert.eq(coll.find({"meta.y": 2}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 1);
assert.eq(coll.find({"meta.y": 2}).itcount(), 0);
}
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({"meta.y": 1}).itcount(), 1);
},
admin: true,
},
// The second update will fail, and this is an ordered batch so the final update
// should
// not execute.
failure: {
req: {
bulkWrite: 1,
ops: [
{
update: 0,
filter: {"meta.x": {$gte: -20}},
updateMods: {$set: {"meta.y": 5}},
multi: true,
},
{
update: 0,
filter: {"meta.x": -22},
updateMods: {$set: {time: "deadbeef"}},
multi: true,
},
{
update: 0,
filter: {"meta.x": -22},
updateMods: {$set: {"meta.y": 4}},
multi: true,
},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(
coll.insert([
{meta: {x: -22}, time: ISODate()},
{meta: {x: -20}, time: ISODate()},
{meta: {x: 20}, time: ISODate()},
]),
);
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 2);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.includes([ErrorCodes.BadValue, ErrorCodes.InvalidOptions], res.cursor.firstBatch[1].code);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 2);
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({"meta.y": 5}).itcount(), 2);
assert.eq(coll.find({"meta.y": 4}).itcount(), 0);
},
admin: true,
},
},
};
// A list of additional CRUD ops which exercise different write paths, and do error handling
// differently than the basic write path exercised in the test cases above.
let additionalCRUDOps = {
"deleteMany": {
noop: {
req: {delete: collName, deletes: [{q: {a: {$lt: 0}}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.insert({a: 2}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 0);
assert.eq(coll.find().itcount(), 2);
},
},
success: {
req: {delete: collName, deletes: [{q: {a: {$gt: -5}}, limit: 0}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.insert({a: -1}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 2);
assert.eq(coll.find().itcount(), 0);
},
},
},
"insertMany": {
success: {
req: {insert: collName, documents: [{a: -2}, {a: 2}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({_id: 1}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 2);
assert.eq(coll.find().itcount(), 3);
},
},
failure: {
req: {insert: collName, documents: [{a: -2}, {a: 2}], ordered: false},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {x: {$exists: true}}}));
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 2);
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.writeErrors[1].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.n, 0);
assert.eq(coll.find().itcount(), 1);
},
},
},
"updateMany": {
noop: {
req: {update: collName, updates: [{q: {a: {$gt: -21}}, u: {$set: {b: 1}}, multi: true}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.commandWorked(coll.remove({a: {$gt: -21}}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 0);
assert.eq(res.n, 0);
assert.eq(coll.find({b: 1}).toArray().length, 0);
},
},
success: {
req: {update: collName, updates: [{q: {a: {$gt: -21}}, u: {$set: {b: 1}}, multi: true}]},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 3);
assert.eq(res.n, 3);
assert.eq(coll.find({b: 1}).toArray().length, 3);
},
},
failure: {
req: {update: collName, updates: [{q: {a: {$gt: -5}}, u: {$set: {b: 1}}, multi: true}]},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.insert({a: 2}));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.n, 0);
assert.eq(res.nModified, 0);
assert.eq(coll.count({b: 1}), 0);
},
},
},
"findOneAndRemove": {
noop: {
req: {findAndModify: collName, query: {a: 1}, remove: true},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.remove({a: 1}));
assert.eq(coll.find().itcount(), 0);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
}
assert.eq(res.value, null);
assert.eq(coll.find().itcount(), 0);
},
},
success: {
req: {findAndModify: collName, query: {a: 1}, remove: true},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.value.a, 1);
assert.eq(coll.find().itcount(), 0);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find().itcount(), 1);
}
},
},
},
"findOneAndUpdate": {
// Modifier updates
noop: {
req: {findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorkedIgnoringWriteConcernErrors(
coll.getDB().runCommand({findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}}),
);
assert.eq(coll.count({a: 1, c: 2}), 1);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.lastErrorObject.updatedExisting, true);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
}
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({a: 1, c: 2}), 1);
},
},
success: {
req: {findAndModify: collName, query: {a: 1}, update: {$set: {c: 2}}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1}));
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.lastErrorObject.updatedExisting, true);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll.count({a: 1, c: 2}), 1);
} else {
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.count({a: 1, c: 2}), 0);
}
},
},
failure: {
req: {findAndModify: collName, query: {a: 1}, update: {$set: {value: 3}}},
setupFunc: (coll) => {
assert.commandWorked(coll.insert({a: 1, value: 1}));
assert.eq(coll.find().itcount(), 1);
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {value: {$gt: 4}}}));
},
confirmFunc: (res, coll) => {
assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure);
assert.eq(coll.find({a: 1, value: 1}).itcount(), 1);
},
},
},
"unorderedBatch": {
noop: {
// The two writes would execute the same update, so one will be a no-op.
req: {
update: collName,
updates: [
{q: {a: 21}, u: {$set: {b: 1}}},
{q: {a: {$gte: -20}}, u: {$set: {b: 1}}, multi: true},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22, b: 1}, {a: -20, b: 1}, {a: 20, b: 1}, {a: 21}]));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.nModified, 1);
assert.eq(coll.find({b: 1}).toArray().length, 4);
},
},
success: {
// All updates should succeed, but all should return WCEs.
req: {
update: collName,
updates: [
{q: {a: -20}, u: {$set: {b: 1}}},
{q: {a: 20}, u: {$set: {b: 1}}},
{q: {a: 21}, u: {$set: {b: 1}}},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.n, 3);
assert.eq(res.nModified, 3);
assert.eq(coll.find({b: 1}).toArray().length, 3);
} else {
// The two phase write path returns WriteConcernTimeout in the write
// errors array.
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 3);
res.writeErrors.forEach((err) => {
assert.eq(err.code, ErrorCodes.WriteConcernTimeout);
});
assert.eq(res.nModified, 0);
assert.eq(coll.find({b: 1}).toArray().length, 0);
}
},
},
failure: {
// The second update should fail the validator. This is an unordered batch, so the
// other two updates should succeed, but still return WCEs.
req: {
update: collName,
updates: [
{q: {a: -20}, u: {$set: {b: 3}}},
{q: {a: 20}, u: {$set: {b: 1}}},
{q: {a: 21}, u: {$set: {b: 3}}},
],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.nModified, 2);
assert.eq(coll.find().itcount(), 4);
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 2);
} else {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 3);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.writeErrors[1].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.writeErrors[2].code, ErrorCodes.WriteConcernTimeout);
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 0);
}
},
},
},
"orderedBatch": {
noop: {
// The last update is a no-op.
req: {
update: collName,
updates: [
{q: {a: {$gte: -21}}, u: {$set: {b: 1}}, multi: true},
{q: {a: 21}, u: {$set: {b: 1}}},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22, b: 1}, {a: -20, b: 1}, {a: 20, b: 1}, {a: 21}]));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.nModified, 1);
assert.eq(coll.find({b: 1}).itcount(), 4);
},
},
success: {
// All updates should succeed, but all should return WCEs.
req: {
update: collName,
updates: [
{q: {a: -20}, u: {$set: {b: 1}}},
{q: {a: 20}, u: {$set: {b: 1}}},
{q: {a: 21}, u: {$set: {b: 1}}},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.nModified, 3);
assert.eq(coll.find({b: 1}).toArray().length, 3);
} else {
// We stop execution after the first write because it fails with a WCE
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nModified, 0);
assert.eq(coll.find({b: 1}).toArray().length, 0);
}
},
},
failure: {
// The second update should fail the validator. This is an ordered batch, so the
// last update should not be executed. The first and second should still return
// WCEs.
req: {
update: collName,
updates: [
{q: {a: -20}, u: {$set: {b: 3}}},
{q: {a: 20}, u: {$set: {b: 1}}},
{q: {a: 21}, u: {$set: {b: 3}}},
],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}, {a: 21}]));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
assert.eq(coll.find().itcount(), 4);
},
confirmFunc: (res, coll, cluster, clusterType) => {
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.nModified, 1);
assert.eq(coll.find().itcount(), 4);
assert.eq(coll.find({b: {$exists: true}}).toArray().length, 1);
} else {
// We stop execution after the first write because it fails with a WCE
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert(res.writeErrors && res.writeErrors.length == 1);
assert.eq(res.writeErrors[0].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nModified, 0);
assert.eq(coll.find({b: 1}).toArray().length, 0);
}
},
},
},
"bulkWriteUnordered": {
// The two writes would execute the same delete, so one will be a no-op.
noop: {
req: {
bulkWrite: 1,
ops: [
{delete: 0, filter: {a: {$gte: -20}}, multi: true},
{delete: 0, filter: {a: -20}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}]));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
}
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.nDeleted, 1);
assert.eq(coll.find().itcount(), 1);
},
admin: true,
},
success: {
req: {
bulkWrite: 1,
ops: [
{insert: 0, document: {a: 22}},
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 1);
assert.eq(coll.find({b: 2}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 0);
assert.eq(coll.find({b: 2}).itcount(), 0);
}
assert.eq(res.nInserted, 1);
assert.eq(coll.find().itcount(), 4);
},
admin: true,
},
failure: {
// The second update should fail, but this is an unordered batch so the other 2
// updates should succeed.
req: {
bulkWrite: 1,
ops: [
{update: 0, filter: {a: {$gte: -20}}, updateMods: {$set: {b: 5}}, multi: true},
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
{update: 0, filter: {a: -22}, updateMods: {$set: {b: 4}}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: false,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 3);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 2);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.DocumentValidationFailure);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[2].ok, 1);
assert.eq(res.cursor.firstBatch[2].n, 1);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 3);
assert.eq(coll.find({b: 4}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[2].ok, 0);
assert.eq(res.cursor.firstBatch[2].n, 0);
assert.eq(res.cursor.firstBatch[2].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 2);
assert.eq(res.nModified, 2);
assert.eq(coll.find({b: 4}).itcount(), 0);
}
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({b: 5}).itcount(), 2);
assert.eq(coll.find({b: 2}).itcount(), 0);
},
admin: true,
},
},
"bulkWriteOrdered": {
// The two deletes would remove the same doc, so one will be a no-op.
noop: {
req: {
bulkWrite: 1,
ops: [
{delete: 0, filter: {a: {$gte: -20}}, multi: true},
{delete: 1, filter: {a: -20}},
{insert: 0, document: {a: 25}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}]));
assert.eq(coll.find().itcount(), 2);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch.length, 3);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.cursor.firstBatch[2].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(coll.find().itcount(), 2);
} else {
// The write without shard key will execute a transaction and fail the
// write with WriteConcernTimeout, so the insert will not execute.
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 1);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].n, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(coll.find().itcount(), 1);
}
},
admin: true,
},
success: {
req: {
bulkWrite: 1,
ops: [
{update: 0, filter: {a: {$gte: -10}}, updateMods: {$set: {b: 1}}, multi: true},
{update: 1, filter: {a: -20}, updateMods: {$set: {b: 2}}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
let sk = getShardKey(coll, fullNs);
let writeWithoutSkOrId = bsonWoCompare(sk, {"a": 1}) != 0 && bsonWoCompare(sk, {}) != 0;
if (clusterType != "sharded" || !writeWithoutSkOrId) {
assert.eq(res.cursor.firstBatch[1].ok, 1);
assert.eq(res.nErrors, 0);
assert.eq(res.nModified, 2);
assert.eq(coll.find({b: 2}).itcount(), 1);
} else {
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.WriteConcernTimeout);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 1);
assert.eq(coll.find({b: 2}).itcount(), 0);
}
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({b: 1}).itcount(), 1);
},
admin: true,
},
// The second update will fail, and this is an ordered batch so the final update
// should
// not execute.
failure: {
req: {
bulkWrite: 1,
ops: [
{update: 0, filter: {a: {$gte: -20}}, updateMods: {$set: {b: 5}}, multi: true},
{update: 0, filter: {a: -20}, updateMods: {$set: {b: 2}}},
{update: 0, filter: {a: -22}, updateMods: {$set: {b: 4}}},
],
nsInfo: [{ns: fullNs}, {ns: fullNs}, {ns: fullNs}],
ordered: true,
},
setupFunc: (coll, cluster, clusterType, secondariesRunning, optionalArgs) => {
assert.commandWorked(coll.insert([{a: -22}, {a: -20}, {a: 20}]));
assert.commandWorked(coll.getDB().runCommand({collMod: collName, validator: {b: {$gt: 2}}}));
assert.eq(coll.find().itcount(), 3);
},
confirmFunc: (res, coll, cluster, clusterType) => {
assert.commandWorkedIgnoringWriteErrorsAndWriteConcernErrors(res);
assert.eq(res.cursor.firstBatch.length, 2);
assert.eq(res.cursor.firstBatch[0].ok, 1);
assert.eq(res.cursor.firstBatch[0].n, 2);
assert.eq(res.cursor.firstBatch[1].ok, 0);
assert.eq(res.cursor.firstBatch[1].code, ErrorCodes.DocumentValidationFailure);
assert.eq(res.nErrors, 1);
assert.eq(res.nModified, 2);
assert.eq(coll.find().itcount(), 3);
assert.eq(coll.find({b: 5}).itcount(), 2);
assert.eq(coll.find({b: 4}).itcount(), 0);
},
admin: true,
},
},
};
export function stopSecondaries(cluster, clusterType) {
if (clusterType == "rs") {
assert.eq(cluster.nodeList().length, 3);
// Stop one of the secondaries so that w:3 will fail
const secondary = cluster.getSecondaries()[0];
cluster.stop(secondary);
return [secondary];
} else {
assert.eq(clusterType, "sharded");
let secondariesRunning = [];
const shards = cluster.getAllShards();
shards.forEach((rs) => {
assert.eq(rs.nodeList().length, 3);
// Stop one of the secondaries so that w:3 will fail. Some commands override user
// write concern to be w: majority. For those specific test cases, we'll need to
// shut down the other secondary to ensure waiting for write concern will still
// fail, so we track the other secondary.
const secondaries = rs.getSecondaries();
rs.stop(secondaries[0]);
secondariesRunning.push(secondaries[1]);
});
return secondariesRunning;
}
}
export function restartSecondaries(cluster, clusterType) {
if (clusterType == "rs") {
// Restart the secondary
cluster.restart(cluster.getSecondaries()[0]);
cluster.waitForPrimary();
cluster.awaitSecondaryNodes();
} else {
// Restart the secondaries
const shards = cluster.getAllShards();
shards.forEach((rs) => {
const secondaries = rs.getSecondaries();
rs.restart(secondaries[0]);
rs.waitForPrimary();
rs.awaitSecondaryNodes();
});
}
}
function awaitReplication(cluster, clusterType) {
if (clusterType == "rs") {
cluster.awaitReplication();
} else {
assert.eq(clusterType, "sharded");
const shards = cluster.getAllShards();
shards.forEach((rs) => {
rs.awaitReplication();
});
}
}
/**
* Assert that the result contains a write concern error. Typically we expect WCEs to be
* reported in the WriteConcernErrors field, but some commands nest the WCE and other actually
* return the error as a top-level command error.
*/
export function assertHasWCE(res, cmd) {
try {
assertWriteConcernError(res);
} catch (e) {
try {
// Some commands return WCEs in a nested "raw" field
if (res.raw) {
let hasWCE = false;
Object.keys(res.raw).forEach((key) => {
if (res.raw[key].writeConcernError) {
assertWriteConcernError(res.raw[key]);
hasWCE = true;
}
});
assert(hasWCE);
} else {
// Some commands fail the command with WriteConcernTimeout as a top-level error,
// and do not include a separate WCE field.
assert.commandFailedWithCode(res, ErrorCodes.WriteConcernTimeout);
}
} catch (err) {
throw e;
}
}
}
function runCommandTest(testCase, conn, coll, cluster, clusterType, preSetup, secondariesRunning, forceUseMajorityWC) {
const dbName = coll.getDB().getName();
// Drop collection.
coll.drop();
assert.eq(0, coll.find().itcount(), "test collection not empty");
jsTestLog("Testing command: " + tojson(testCase.req));
// Create environment for command to run in.
if (preSetup) {
preSetup(conn, cluster, dbName, collName);
}
let optionalArgs = {};
testCase.setupFunc(coll, cluster, clusterType, secondariesRunning, optionalArgs);
const request =
typeof testCase.req === "function" ? testCase.req(cluster, coll) : Object.assign({}, testCase.req, {});
// Provide a small wtimeout that we expect to time out.
if (forceUseMajorityWC) {
request.writeConcern = {w: "majority", wtimeout: 1000};
} else {
request.writeConcern = {w: 3, wtimeout: 1000};
}
// We run the command on a different connection. If the the command were run on the
// same connection, then the client last op for the noop write would be set by the setup
// operation. By using a fresh connection the client last op begins as null.
// This test explicitly tests that write concern for noop writes works when the
// client last op has not already been set by a duplicate operation.
const freshConn = new Mongo(conn.host);
// We check the error code of 'res' in the 'confirmFunc'.
const res = testCase.admin ? freshConn.adminCommand(request) : freshConn.getDB(dbName).runCommand(request);
try {
// Tests that the command receives a write concern error.
assertHasWCE(res, testCase.req);
// Validate post-conditions of the commands.
jsTestLog(tojson(getCommandName(request)) + " command returned " + tojson(res));
testCase.confirmFunc(res, coll, cluster, clusterType, secondariesRunning, optionalArgs);
} catch (e) {
// Make sure that we print out the response.
printjson(res);
throw e;
}
// Kill the implicit session to abort any idle transactions to "force" reap.
assert.commandWorkedOrFailedWithCode(
freshConn.adminCommand({killSessions: [freshConn.getDB(dbName).getSession().getSessionId()]}),
ErrorCodes.HostUnreachable,
);
}
// TODO SERVER-97736 Modify `shouldSkipTestCase` to ensure these commands are not skipped once
// they no longer hang until the majority of the shards involved in DDL are available and return
// WCE on timing out.
const shardedDDLCommandsRequiringMajorityCommit = [
"changePrimary",
"collMod",
"convertToCapped",
"drop",
"dropDatabase",
"dropIndexes",
"movePrimary",
"refineCollectionShardKey",
"renameCollection",
"setAllowMigrations",
"shardCollection",
];
function shouldSkipTestCase(clusterType, command, testCase, shardedCollection, writeWithoutSk, coll) {
if (
!shardedCollection &&
(command == "moveChunk" ||
command == "moveRange" ||
command == "refineCollectionShardKey" ||
command == "setAllowMigrations" ||
command == "updateDocSk")
) {
jsTestLog(
"Skipping " + command + " because requires sharded collection, and the current collection is not sharded.",
);
return true;
}
if (shardedCollection && command == "updateDocSk" && bsonWoCompare(getShardKey(coll, fullNs), {"_id": 1}) == 0) {
jsTestLog(
"Skipping updating a document's shard key because the shard key is {_id: 1}, and the _id field is immutable.",
);
return true;
}
if (shardedCollection && command == "shardCollection") {
jsTestLog(
"Skipping " + command + " because requires an unsharded collection, and the current collection is sharded.",
);
return true;
}
// TODO SERVER-112609 Re-enable create command for sharded collections once no-op operations
// honor a 'majority' write concern.
if (shardedCollection && command == "create") {
jsTestLog(
"Skipping " +
command +
" because the preSetup function creates a sharded collection and all test cases become no-op operations.",
);
return true;
}
if (testCase == "noop") {
// TODO SERVER-100309 adapt/enable setFeatureCompatibilityVersion no-op case once the
// upgrade procedure will not proactively shard the sessions collection.
if (
clusterType == "sharded" &&
(shardedDDLCommandsRequiringMajorityCommit.includes(command) ||
command == "setFeatureCompatibilityVersion" ||
// TODO SERVER-112609 Re-enable create command no-op test in sharded clusters once
// no-op operations honor a 'majority' write concern.
command == "create")
) {
jsTestLog("Skipping " + command + " test for no-op case.");
return true;
}
}
if (testCase == "success") {
if (clusterType == "sharded" && shardedDDLCommandsRequiringMajorityCommit.includes(command)) {
jsTestLog("Skipping " + command + " test for success case.");
return true;
}
}
if (testCase == "failure") {
if (clusterType == "sharded" && shardedDDLCommandsRequiringMajorityCommit.includes(command)) {
jsTestLog("Skipping " + command + " test for failure case.");
return true;
}
if (clusterType == "rs" && command == "setDefaultRWConcern") {
jsTestLog("Skipping " + command + " test for failure case.");
return true;
}
}
}
// These commands only accept w:1 or w:majority.
let umcRequireMajority = [
"createRole",
"createUser",
"dropAllRolesFromDatabase",
"dropAllUsersFromDatabase",
"dropRole",
"dropUser",
"grantPrivilegesToRole",
"grantRolesToRole",
"grantRolesToUser",
"revokePrivilegesFromRole",
"revokeRolesFromRole",
"revokeRolesFromUser",
"updateRole",
"updateUser",
];
function executeWriteConcernBehaviorTests(
conn,
coll,
cluster,
clusterType,
preSetup,
commandsToRun,
masterCommandsList,
secondariesRunning,
shardedCollection,
writeWithoutSk,
) {
commandsToRun.forEach((command) => {
let cmd = masterCommandsList[command];
if (!cmd.skip && !cmd.noop && !cmd.success && !cmd.failure) {
throw "Must implement test case for command " + command + ", or explain why it should be skipped.";
}
// Some commands only allow w:1 or w:majority in a sharded cluster, so we must choose
// majority
let forceUseMajorityWC = clusterType == "sharded" && umcRequireMajority.includes(command);
if (cmd.noop) {
if (!shouldSkipTestCase(clusterType, command, "noop", shardedCollection, writeWithoutSk, coll))
runCommandTest(
cmd.noop,
conn,
coll,
cluster,
clusterType,
preSetup,
secondariesRunning,
forceUseMajorityWC,
);
}
if (cmd.success) {
if (!shouldSkipTestCase(clusterType, command, "success", shardedCollection, writeWithoutSk, coll))
runCommandTest(
cmd.success,
conn,
coll,
cluster,
clusterType,
preSetup,
secondariesRunning,
forceUseMajorityWC,
);
}
if (cmd.failure) {
if (!shouldSkipTestCase(clusterType, command, "failure", shardedCollection, writeWithoutSk, coll))
runCommandTest(
cmd.failure,
conn,
coll,
cluster,
clusterType,
preSetup,
secondariesRunning,
forceUseMajorityWC,
writeWithoutSk,
);
}
});
}
export function checkWriteConcernBehaviorForAllCommands(
conn,
cluster,
clusterType,
preSetup,
shardedCollection,
limitToTimeseriesViews = false,
) {
jsTestLog("Checking write concern behavior for all commands");
const commandsToTest = limitToTimeseriesViews ? wcTimeseriesViewsCommandsTests : wcCommandsTests;
const commandsList = AllCommandsTest.checkCommandCoverage(conn, commandsToTest);
let coll = conn.getDB(dbName).getCollection(collName);
if (clusterType == "rs") {
jsTestLog("Running tests against replica set");
stopSecondaries(cluster, clusterType);
executeWriteConcernBehaviorTests(conn, coll, cluster, clusterType, preSetup, commandsList, commandsToTest);
restartSecondaries(cluster, clusterType);
awaitReplication(cluster, clusterType);
return;
}
assert.eq(clusterType, "sharded");
// In a sharded cluster, some commands are coordinated through the configsvr, and others
// through the shard(s). For commands that target the configsvr, we'll prevent replication
// on the configsvr. Otherwise, we'll prevent replication on the shard(s). To speed up the
// execution of this test, we'll run all commands that target the configsvr first and then
// those that shards target shards so that we don't need to stop and restart nodes for each
// test case.
let cmdsTargetConfigServer = [];
let cmdsTargetShards = [];
commandsList.forEach((command) => {
let cmd = commandsToTest[command];
if (cmd.targetConfigServer) {
cmdsTargetConfigServer.push(command);
} else {
cmdsTargetShards.push(command);
}
});
if (FeatureFlagUtil.isPresentAndEnabled(cluster.configRS.getPrimary(), "CreateDatabaseDDLCoordinator")) {
shardedDDLCommandsRequiringMajorityCommit.push("enableSharding");
}
// Run test cases for commands that target the configsvr
(() => {
jsTestLog("Running commands that target the configsvr");
assert.eq(cluster.configRS.nodeList().length, 3);
// Stop one of the secondaries so that w:3 will fail. Some commands override user write
// concern to be w: majority. For those specific test cases, we'll need to shut down the
// other secondary to ensure wiating for write concern will still fail.
const csrsSecondaries = cluster.configRS.getSecondaries();
cluster.configRS.stop(csrsSecondaries[0]);
executeWriteConcernBehaviorTests(
conn,
coll,
cluster,
clusterType,
preSetup,
cmdsTargetConfigServer,
commandsToTest,
[csrsSecondaries[1]],
shardedCollection,
);
cluster.configRS.restart(csrsSecondaries[0]);
})();
// Run test cases for commands the target shard(s)
(() => {
jsTestLog("Running commands that target shards");
let secondariesRunning = stopSecondaries(cluster, clusterType);
executeWriteConcernBehaviorTests(
conn,
coll,
cluster,
clusterType,
preSetup,
cmdsTargetShards,
commandsToTest,
secondariesRunning,
shardedCollection,
);
restartSecondaries(cluster, clusterType);
})();
awaitReplication(cluster, clusterType);
}
export function checkWriteConcernBehaviorAdditionalCRUDOps(
conn,
cluster,
clusterType,
preSetup,
shardedCollection,
writeWithoutSk,
limitToTimeseriesViews = false,
) {
jsTestLog("Checking write concern behavior for additional CRUD commands");
let coll = conn.getDB(dbName).getCollection(collName);
const commandsToTest = limitToTimeseriesViews ? additionalCRUDOpsTimeseriesViews : additionalCRUDOps;
stopSecondaries(cluster, clusterType);
executeWriteConcernBehaviorTests(
conn,
coll,
cluster,
clusterType,
preSetup,
Object.keys(commandsToTest),
commandsToTest,
[] /* secondariesRunning */,
shardedCollection,
);
restartSecondaries(cluster, clusterType);
awaitReplication(cluster, clusterType);
}
export function checkWriteConcernBehaviorUpdatingDocShardKey(
conn,
cluster,
clusterType,
preSetup,
shardedCollection,
writeWithoutSk,
limitToTimeseriesViews = false,
) {
jsTestLog("Checking write concern behavior for updating a document's shard key");
let coll = conn.getDB(dbName).getCollection(collName);
stopSecondaries(cluster, clusterType);
// Note we don't need to check the transaction cases, because we will not wait for write concern
// on the individual transaction statements, but rather only on the commit. We test commit
// already separately above. We test the retryable write case because the server internally
// executes commit in this case, and builds a response object from the commit response.
let testCases = {
"updateDocSk": {
noop: {
req: () => ({
update: collName,
updates: [{q: {a: 1}, u: {$set: {[Object.keys(getShardKey(coll, fullNs))[0]]: -1}}}],
lsid: getLSID(),
txnNumber: getTxnNumber(),
}),
setupFunc: (coll) => {
let sk = getShardKey(coll, fullNs);
if (bsonWoCompare(sk, {a: 1}) == 0) {
assert.commandWorked(coll.insert({a: 1}));
assert.commandWorked(coll.remove({a: 1}));
} else {
// Make sure doc has "a" to query on and the shard key
assert.commandWorked(coll.insert({a: 1, [Object.keys(sk)[0]]: 2}));
assert.commandWorked(coll.remove({[Object.keys(sk)[0]]: 2}));
}
assert.eq(coll.find().itcount(), 0);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find().itcount(), 0);
genNextTxnNumber();
},
},
success: {
req: () => ({
update: collName,
updates: [{q: {a: 1}, u: {$set: {[Object.keys(getShardKey(coll, fullNs))[0]]: -1}}}],
lsid: getLSID(),
txnNumber: getTxnNumber(),
}),
setupFunc: (coll) => {
let sk = getShardKey(coll, fullNs);
if (bsonWoCompare(sk, {a: 1}) == 0) {
assert.commandWorked(coll.insert({a: 1}));
} else {
// Make sure doc has "a" to query on and the shard key
assert.commandWorked(coll.insert({a: 1, [Object.keys(sk)[0]]: 2}));
}
assert.eq(coll.find().itcount(), 1);
},
confirmFunc: (res, coll) => {
assert.commandWorkedIgnoringWriteConcernErrors(res);
assert.eq(coll.find({[Object.keys(getShardKey(coll, fullNs))[0]]: -1}).itcount(), 1);
genNextTxnNumber();
},
},
// We don't test a failure case, because if the insert or delete (executed internally by
// the server) fails for any reason, mongod will not wait for write concern at all
// (again, because it only waits on commit or abort, and in this case we won't try to
// commit). So, this becomes a similar case as the commit case in the tests above -
// there isn't a sequence of events that would mutate the data that would cause the
// commit to fail in such a way that the WCE is important.
},
};
executeWriteConcernBehaviorTests(
conn,
coll,
cluster,
clusterType,
preSetup,
Object.keys(testCases),
testCases,
[] /* secondariesRunning */,
shardedCollection,
writeWithoutSk,
limitToTimeseriesViews,
);
restartSecondaries(cluster, clusterType);
awaitReplication(cluster, clusterType);
}
export function precmdShardKey(shardKey, conn, cluster, dbName, collName) {
let db = conn.getDB(dbName);
let nss = dbName + "." + collName;
assert.commandWorked(db.adminCommand({enableSharding: dbName, primary: cluster.shard0.shardName}));
assert.commandWorked(db.adminCommand({shardCollection: nss, key: {[shardKey]: 1}}));
assert.commandWorked(db.adminCommand({split: nss, middle: {[shardKey]: 0}}));
assert.commandWorked(
db.adminCommand({
moveChunk: nss,
find: {[shardKey]: -1},
to: cluster.shard0.shardName,
_waitForDelete: true,
}),
);
assert.commandWorked(
db.adminCommand({
moveChunk: nss,
find: {[shardKey]: 1},
to: cluster.shard1.shardName,
_waitForDelete: true,
}),
);
}