258 lines
12 KiB
JavaScript
258 lines
12 KiB
JavaScript
/**
|
|
* Test that mongod will not allow creation of collection validators using new query features when
|
|
* the feature compatibility version is older than the latest version.
|
|
*
|
|
* We restart mongod during the test and expect it to have the same data after restarting.
|
|
* @tags: [requires_persistence]
|
|
*/
|
|
|
|
const testName = "collection_validator_feature_compatibility_version";
|
|
const dbpath = MongoRunner.dataPath + testName;
|
|
|
|
// An array of feature flags that must be enabled to run feature flag tests.
|
|
const featureFlagsToEnable = [];
|
|
|
|
// These arrays should be populated with
|
|
//
|
|
// { validator: { ... }, nonMatchingDocument: { ... }, lastStableErrCode }
|
|
//
|
|
// objects that use query features in new versions of mongod. Note that this also
|
|
// includes new aggregation expressions able to be used with the $expr match expression. This
|
|
// test ensures that a collection validator accepts the new query feature when the feature
|
|
// compatibility version is the latest version, and rejects it when the feature compatibility
|
|
// version is the last version.
|
|
// The 'lastStableErrCode' field indicates what error the last version would throw when
|
|
// parsing the validator.
|
|
const testCasesLastContinuous = [
|
|
//
|
|
// Populate with any new expressions.
|
|
//
|
|
];
|
|
const testCasesLastContinuousWithFeatureFlags = [
|
|
|
|
];
|
|
|
|
const testCasesLastStable = testCasesLastContinuous.concat([]);
|
|
const testCasesLastStableWithFeatureFlags = testCasesLastContinuousWithFeatureFlags.concat([]);
|
|
|
|
// Tests Feature Compatibility Version behavior of the validator of a collection by executing test
|
|
// cases 'testCases' and using a previous stable version 'lastVersion' of mongod. 'lastVersion' can
|
|
// have values "last-lts" and "last-continuous".
|
|
function testCollectionValidatorFCVBehavior(lastVersion, testCases, featureFlags = []) {
|
|
if (testCases.length === 0) {
|
|
jsTestLog("Skipping setup for tests against " + lastVersion + " since there are none");
|
|
return;
|
|
}
|
|
|
|
let conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "latest"});
|
|
assert.neq(null, conn, "mongod was unable to start up");
|
|
|
|
let testDB = conn.getDB(testName);
|
|
for (let i = 0; i < featureFlags.length; i++) {
|
|
const command = {"getParameter": 1};
|
|
command[featureFlags[i]] = 1;
|
|
const featureEnabled =
|
|
assert.commandWorked(testDB.adminCommand(command))[featureFlags[i]].value;
|
|
if (!featureEnabled) {
|
|
jsTestLog("Skipping test because the " + featureFlags[i] + " feature flag is disabled");
|
|
MongoRunner.stopMongod(conn);
|
|
return;
|
|
}
|
|
}
|
|
|
|
let adminDB = conn.getDB("admin");
|
|
|
|
// Explicitly set the feature compatibility version to the latest version.
|
|
assert.commandWorked(
|
|
adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}));
|
|
|
|
testCases.forEach(function(test, i) {
|
|
// Create a collection with a validator using new query features.
|
|
const coll = testDB["coll" + i];
|
|
assert.commandWorked(
|
|
testDB.createCollection(coll.getName(), {validator: test.validator}),
|
|
`Expected to be able to create collection with validator ${tojson(test.validator)}`);
|
|
|
|
// The validator should cause this insert to fail.
|
|
assert.writeErrorWithCode(
|
|
coll.insert(test.nonMatchingDocument),
|
|
ErrorCodes.DocumentValidationFailure,
|
|
`Expected document ${tojson(test.nonMatchingDocument)} to fail validation for ` +
|
|
`collection with validator ${tojson(test.validator)}`);
|
|
|
|
// Set a validator using new query features on an existing collection.
|
|
coll.drop();
|
|
assert.commandWorked(testDB.createCollection(coll.getName()));
|
|
assert.commandWorked(
|
|
testDB.runCommand({collMod: coll.getName(), validator: test.validator}),
|
|
`Expected to be able to modify collection validator to be ${tojson(test.validator)}`);
|
|
|
|
// Another failing update.
|
|
assert.writeErrorWithCode(
|
|
coll.insert(test.nonMatchingDocument),
|
|
ErrorCodes.DocumentValidationFailure,
|
|
`Expected document ${tojson(test.nonMatchingDocument)} to fail validation for ` +
|
|
`collection with validator ${tojson(test.validator)}`);
|
|
});
|
|
|
|
// Set the feature compatibility version to the last version.
|
|
assert.commandWorked(adminDB.runCommand(
|
|
{setFeatureCompatibilityVersion: binVersionToFCV(lastVersion), confirm: true}));
|
|
|
|
testCases.forEach(
|
|
function(test, i) {
|
|
// The validator is already in place, so it should still cause this insert to fail.
|
|
const coll = testDB["coll" + i];
|
|
assert.writeErrorWithCode(
|
|
coll.insert(test.nonMatchingDocument),
|
|
ErrorCodes.DocumentValidationFailure,
|
|
`Expected document ${tojson(test.nonMatchingDocument)} to fail validation for ` +
|
|
`collection with validator ${tojson(test.validator)}`);
|
|
|
|
// Trying to create a new collection with a validator using new query features should
|
|
// fail while feature compatibility version is the last version.
|
|
let res = testDB.createCollection("other", {validator: test.validator});
|
|
assert.commandFailedWithCode(
|
|
res,
|
|
ErrorCodes.QueryFeatureNotAllowed,
|
|
'Expected *not* to be able to create collection with validator ' +
|
|
tojson(test.validator));
|
|
assert(res.errmsg.match(/feature compatibility version/),
|
|
`Expected error message from createCollection with validator ` +
|
|
`${tojson(test.validator)} to reference 'feature compatibility version' but got: ` +
|
|
res.errmsg);
|
|
|
|
// Trying to update a collection with a validator using new query features should also
|
|
// fail.
|
|
res = testDB.runCommand({collMod: coll.getName(), validator: test.validator});
|
|
assert.commandFailedWithCode(res,
|
|
ErrorCodes.QueryFeatureNotAllowed,
|
|
`Expected to be able to create collection with validator ${
|
|
tojson(test.validator)}`);
|
|
assert(res.errmsg.match(/feature compatibility version/),
|
|
`Expected error message from createCollection with validator ` +
|
|
`${tojson(test.validator)} to reference 'feature compatibility version' but got: ` +
|
|
res.errmsg);
|
|
});
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
|
|
if (testCases.length > 0) {
|
|
// Versions of mongod 4.2 and later are able to start up with a collection validator that's
|
|
// considered invalid. However, any writes to the collection will fail.
|
|
conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: lastVersion, noCleanData: true});
|
|
assert.neq(
|
|
null, conn, lastVersion + " mongod was unable to start up with invalid validator");
|
|
const testDB = conn.getDB(testName);
|
|
|
|
// Check that writes fail to all collections with validators using new query features.
|
|
testCases.forEach(function(test, i) {
|
|
const coll = testDB["coll" + i];
|
|
assert.commandFailedWithCode(coll.insert({foo: 1}), test.lastStableErrCode);
|
|
});
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
}
|
|
|
|
// Starting up the latest version of mongod, however, should succeed, even though the feature
|
|
// compatibility version is still set to the last version.
|
|
conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "latest", noCleanData: true});
|
|
assert.neq(null, conn, "mongod was unable to start up");
|
|
|
|
adminDB = conn.getDB("admin");
|
|
testDB = conn.getDB(testName);
|
|
|
|
// And the validator should still work.
|
|
testCases.forEach(function(test, i) {
|
|
const coll = testDB["coll" + i];
|
|
assert.writeErrorWithCode(
|
|
coll.insert(test.nonMatchingDocument),
|
|
ErrorCodes.DocumentValidationFailure,
|
|
`Expected document ${tojson(test.nonMatchingDocument)} to fail validation for ` +
|
|
`collection with validator ${tojson(test.validator)}`);
|
|
|
|
// Remove the validator.
|
|
assert.commandWorked(testDB.runCommand({collMod: coll.getName(), validator: {}}));
|
|
});
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
|
|
// Now, we should be able to start up the last version of mongod.
|
|
conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: lastVersion, noCleanData: true});
|
|
assert.neq(
|
|
null,
|
|
conn,
|
|
`version ${MongoRunner.getBinVersionFor(lastVersion)} of mongod failed to start, even` +
|
|
" after we removed the validator using new query features");
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
|
|
// The rest of the test uses the latest version of mongod.
|
|
conn = MongoRunner.runMongod({dbpath: dbpath, binVersion: "latest", noCleanData: true});
|
|
assert.neq(null, conn, "mongod was unable to start up");
|
|
|
|
adminDB = conn.getDB("admin");
|
|
testDB = conn.getDB(testName);
|
|
|
|
// Set the feature compatibility version back to the latest version.
|
|
assert.commandWorked(
|
|
adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}));
|
|
|
|
testCases.forEach(function(test, i) {
|
|
const coll = testDB["coll2" + i];
|
|
|
|
// Now we should be able to create a collection with a validator using new query features
|
|
// again.
|
|
assert.commandWorked(
|
|
testDB.createCollection(coll.getName(), {validator: test.validator}),
|
|
`Expected to be able to create collection with validator ${tojson(test.validator)}`);
|
|
|
|
// And we should be able to modify a collection to have a validator using new query
|
|
// features.
|
|
assert.commandWorked(
|
|
testDB.runCommand({collMod: coll.getName(), validator: test.validator}),
|
|
`Expected to be able to modify collection validator to be ${tojson(test.validator)}`);
|
|
});
|
|
|
|
// Set the feature compatibility version to the last version and then restart with
|
|
// internalValidateFeaturesAsPrimary=false.
|
|
assert.commandWorked(adminDB.runCommand(
|
|
{setFeatureCompatibilityVersion: binVersionToFCV(lastVersion), confirm: true}));
|
|
MongoRunner.stopMongod(conn);
|
|
conn = MongoRunner.runMongod({
|
|
dbpath: dbpath,
|
|
binVersion: "latest",
|
|
noCleanData: true,
|
|
setParameter: "internalValidateFeaturesAsPrimary=false"
|
|
});
|
|
assert.neq(null, conn, "mongod was unable to start up");
|
|
|
|
testDB = conn.getDB(testName);
|
|
|
|
testCases.forEach(function(test, i) {
|
|
const coll = testDB["coll3" + i];
|
|
// Even though the feature compatibility version is the last version, we should still
|
|
// be able to add a validator using new query features, because
|
|
// internalValidateFeaturesAsPrimary is false.
|
|
assert.commandWorked(
|
|
testDB.createCollection(coll.getName(), {validator: test.validator}),
|
|
`Expected to be able to create collection with validator ${tojson(test.validator)}`);
|
|
|
|
// We should also be able to modify a collection to have a validator using new query
|
|
// features.
|
|
coll.drop();
|
|
assert.commandWorked(testDB.createCollection(coll.getName()));
|
|
assert.commandWorked(
|
|
testDB.runCommand({collMod: coll.getName(), validator: test.validator}),
|
|
`Expected to be able to modify collection validator to be ${tojson(test.validator)}`);
|
|
});
|
|
|
|
MongoRunner.stopMongod(conn);
|
|
}
|
|
|
|
testCollectionValidatorFCVBehavior("last-lts", testCasesLastStable);
|
|
testCollectionValidatorFCVBehavior(
|
|
"last-lts", testCasesLastStableWithFeatureFlags, featureFlagsToEnable);
|
|
testCollectionValidatorFCVBehavior("last-continuous", testCasesLastContinuous);
|
|
testCollectionValidatorFCVBehavior(
|
|
"last-continuous", testCasesLastContinuousWithFeatureFlags, featureFlagsToEnable); |