mirror of https://github.com/mongodb/mongo
243 lines
8.9 KiB
JavaScript
243 lines
8.9 KiB
JavaScript
/**
|
|
* Tests the behavior of the 'modify' event via various 'collMod' commands.
|
|
*
|
|
* @tags: [
|
|
* requires_fcv_60,
|
|
* # TODO (SERVER-89668): Remove tag. Currently incompatible due to change
|
|
* # events containing the recordIdsReplicated:true option, which
|
|
* # this test dislikes.
|
|
* exclude_when_record_ids_replicated
|
|
* ]
|
|
*/
|
|
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
|
|
import {ChangeStreamTest} from "jstests/libs/query/change_stream_util.js";
|
|
|
|
const testDB = db.getSiblingDB(jsTestName());
|
|
|
|
const dbName = testDB.getName();
|
|
const collName = jsTestName();
|
|
const ns = {
|
|
db: dbName,
|
|
coll: collName,
|
|
};
|
|
|
|
const pipeline = [{$changeStream: {showExpandedEvents: true}}];
|
|
const cst = new ChangeStreamTest(testDB);
|
|
|
|
function getCollectionUuid(coll) {
|
|
const collInfo = testDB.getCollectionInfos({name: coll})[0];
|
|
return collInfo.info.uuid;
|
|
}
|
|
|
|
function assertNextChangeEvent(cursor, expectedEvent) {
|
|
cst.assertNextChangesEqual({cursor: cursor, expectedChanges: expectedEvent});
|
|
}
|
|
|
|
function runTest(startChangeStream) {
|
|
assert.commandWorked(testDB.runCommand({create: collName}));
|
|
assert.commandWorked(testDB[collName].insert({a: 1, b: 1, c: 1}));
|
|
|
|
function testCollModValidator() {
|
|
let cursor = startChangeStream();
|
|
let options = {
|
|
schemaValidator: {
|
|
"$jsonSchema": {
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"a": {
|
|
"bsonType": "number",
|
|
},
|
|
"b": {
|
|
"bsonType": "number",
|
|
},
|
|
"c": {
|
|
"bsonType": "number",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
validationLevel: "strict",
|
|
validationAction: "error",
|
|
};
|
|
|
|
assert.commandWorked(testDB[collName].runCommand({collMod: collName, validator: options.schemaValidator}));
|
|
|
|
const numShards = FixtureHelpers.numberOfShardsForCollection(testDB[collName]);
|
|
|
|
let expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: {validator: options.schemaValidator},
|
|
stateBeforeChange: {
|
|
collectionOptions: {uuid: getCollectionUuid(collName)},
|
|
},
|
|
});
|
|
}
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
|
|
// Modify the validation level.
|
|
const newValidationLevel = "off";
|
|
assert.commandWorked(testDB[collName].runCommand({collMod: collName, validationLevel: newValidationLevel}));
|
|
|
|
expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: {validationLevel: newValidationLevel},
|
|
stateBeforeChange: {
|
|
collectionOptions: {
|
|
uuid: getCollectionUuid(collName),
|
|
validator: options.schemaValidator,
|
|
validationLevel: options.validationLevel,
|
|
validationAction: options.validationAction,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
|
|
options.validationLevel = newValidationLevel;
|
|
|
|
// Modify the validation action, i.e. the parameter which determined whether to error on
|
|
// invalid documents or warn.
|
|
const newValidationAction = "warn";
|
|
assert.commandWorked(testDB[collName].runCommand({collMod: collName, validationAction: newValidationAction}));
|
|
|
|
expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: {validationAction: newValidationAction},
|
|
stateBeforeChange: {
|
|
collectionOptions: {
|
|
uuid: getCollectionUuid(collName),
|
|
validator: options.schemaValidator,
|
|
validationLevel: options.validationLevel,
|
|
validationAction: options.validationAction,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
}
|
|
|
|
function testCollModIndex(key, options) {
|
|
// Determine what "indexName" should be based on "key".
|
|
let arr = [];
|
|
for (let property in key) {
|
|
arr.push(property);
|
|
arr.push(key[property]);
|
|
}
|
|
let indexName = arr.join("_");
|
|
|
|
// Create an index.
|
|
assert.commandWorked(testDB[collName].createIndex(key, options));
|
|
|
|
// Start watching for change stream events.
|
|
let cursor = startChangeStream();
|
|
|
|
// Modify the collection by issuing a command to modify the index, toggling the 'hidden'
|
|
// option on the index. We toggle twice in order to bring back the collection index options
|
|
// into the state it started with.
|
|
const toggleIndexHiddenOp = {
|
|
index: {
|
|
name: indexName,
|
|
hidden: options.hidden ? false : true,
|
|
},
|
|
};
|
|
const undoToggleIndexHiddenOp = {
|
|
index: {
|
|
name: indexName,
|
|
hidden: options.hidden ? true : false,
|
|
},
|
|
};
|
|
assert.commandWorked(testDB[collName].runCommand({collMod: collName, index: toggleIndexHiddenOp.index}));
|
|
assert.commandWorked(testDB[collName].runCommand({collMod: collName, index: undoToggleIndexHiddenOp.index}));
|
|
|
|
const numShards = FixtureHelpers.numberOfShardsForCollection(testDB[collName]);
|
|
|
|
let expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: toggleIndexHiddenOp,
|
|
stateBeforeChange: {
|
|
collectionOptions: {uuid: getCollectionUuid(collName)},
|
|
indexOptions: {hidden: !toggleIndexHiddenOp.index.hidden},
|
|
},
|
|
});
|
|
}
|
|
// First event toggling the 'hidden' option on the index.
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
|
|
expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: undoToggleIndexHiddenOp,
|
|
stateBeforeChange: {
|
|
collectionOptions: {uuid: getCollectionUuid(collName)},
|
|
indexOptions: {hidden: !undoToggleIndexHiddenOp.index.hidden},
|
|
},
|
|
});
|
|
}
|
|
// Second event restoring the 'hidden' option on the index to the initial state.
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
|
|
// Modify the collection by issuing a command to modify the index, this time changing the
|
|
// TTL expiration threshold.
|
|
if (options.expireAfterSeconds) {
|
|
const modifyIndexExpireAfterSecondsOp = {
|
|
index: {name: indexName, expireAfterSeconds: NumberLong(100000)},
|
|
};
|
|
assert.commandWorked(
|
|
testDB[collName].runCommand({collMod: collName, index: modifyIndexExpireAfterSecondsOp.index}),
|
|
);
|
|
|
|
expectedChanges = [];
|
|
for (let i = 0; i < numShards; ++i) {
|
|
expectedChanges.push({
|
|
operationType: "modify",
|
|
ns: ns,
|
|
operationDescription: modifyIndexExpireAfterSecondsOp,
|
|
stateBeforeChange: {
|
|
collectionOptions: {uuid: getCollectionUuid(collName)},
|
|
indexOptions: options,
|
|
},
|
|
});
|
|
}
|
|
assertNextChangeEvent(cursor, expectedChanges);
|
|
}
|
|
|
|
// Drop the index.
|
|
assert.commandWorked(testDB[collName].dropIndexes([indexName]));
|
|
}
|
|
|
|
// Test 'collMod' commands by modifying index options on the collection.
|
|
let options = {};
|
|
testCollModIndex({a: 1}, options);
|
|
|
|
options = {hidden: true};
|
|
testCollModIndex({b: 1}, options);
|
|
|
|
options = {expireAfterSeconds: NumberLong(100000)};
|
|
testCollModIndex({c: 1}, options);
|
|
|
|
// Test 'collMod' commands by modifying validation options on the collection.
|
|
testCollModValidator();
|
|
|
|
testDB[collName].drop();
|
|
}
|
|
|
|
// Run the test using a whole-db collection change stream.
|
|
runTest(() => cst.startWatchingChanges({pipeline, collection: 1}));
|
|
|
|
// Run the test using a single collection change stream.
|
|
runTest(() => cst.startWatchingChanges({pipeline, collection: collName}));
|