diff options
-rw-r--r-- | jstests/multiVersion/exact_top_n_feature_flag.js | 198 | ||||
-rw-r--r-- | jstests/multiVersion/query_feature_flags.js | 248 |
2 files changed, 248 insertions, 198 deletions
diff --git a/jstests/multiVersion/exact_top_n_feature_flag.js b/jstests/multiVersion/exact_top_n_feature_flag.js deleted file mode 100644 index 788427724c8..00000000000 --- a/jstests/multiVersion/exact_top_n_feature_flag.js +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Tests that 'featureFlagExactTopNAccumulator' works correctly. - * - * TODO SERVER-61851 Delete this test once we branch for 6.0. - * - * @tags: [requires_replication, requires_sharding] - */ - -(function() { -"use strict"; - -load("jstests/multiVersion/libs/verify_versions.js"); -load("jstests/multiVersion/libs/multi_rs.js"); // For upgradeSecondaries and upgradeSet. -load("jstests/multiVersion/libs/multi_cluster.js"); // For upgradeCluster. - -function runTest(downgradeVersion) { - const dbName = "db"; - const collName = "exactTopNUpgradeDowngrade"; - - function getOperators(needTopBottom) { - let obj = {needsInputAndN: ["$firstN", "$lastN", "$minN", "$maxN"]}; - if (needTopBottom) { - Object.assign( - obj, {needsOutputAndN: ["$topN", "$bottomN"], needsOutput: ["$top", "$bottom"]}); - } - return obj; - } - - function assertExpectedBehavior(expectedToPass, db) { - function buildOperatorList(needAccumulators) { - let operatorList = []; - for (const [category, opNames] of Object.entries(getOperators(needAccumulators))) { - for (const opName of opNames) { - let spec = {}; - if (category === "needsInputAndN") { - Object.assign(spec, {input: "$a", n: 10}); - } else if (category === "needsOutputAndN") { - Object.assign(spec, {output: "$a", n: 10, sortBy: {b: 1}}); - } else if (category === "needsOutput") { - Object.assign(spec, {output: "$a", sortBy: {b: 1}}); - } - operatorList.push({[opName]: spec}); - } - } - return operatorList; - } - - function createView(pipeline, expectedErrorCodes) { - const viewName = "testView"; - jsTestLog("Attempting to create view: " + tojson(pipeline)); - if (expectedToPass) { - assert.commandWorked(db.createView(viewName, collName, pipeline)); - assert(db[viewName].drop()); - } else { - assert.commandFailedWithCode(db.createView(viewName, collName, pipeline), - expectedErrorCodes); - } - } - - function createValidator(aggExpr, expectedErrorCodes) { - const collMod = {collMod: collName, validator: {$expr: aggExpr}}; - jsTestLog("Attempting to create validator: " + tojson(collMod)); - if (expectedToPass) { - assert.commandWorked(db.runCommand(collMod)); - } else { - assert.commandFailedWithCode(db.runCommand(collMod), expectedErrorCodes); - } - } - - // Accumulators. - for (const accSpec of buildOperatorList(true)) { - createView([{$group: {_id: "$groupKey", output: accSpec}}], - [15952, ErrorCodes.QueryFeatureNotAllowed]); - } - - // Aggregation expressions. - for (const aggExpr of buildOperatorList(false)) { - createView([{$project: {output: aggExpr}}], [31325, ErrorCodes.QueryFeatureNotAllowed]); - createValidator( - aggExpr, [ErrorCodes.InvalidPipelineOperator, ErrorCodes.QueryFeatureNotAllowed]); - } - - // Window functions. - for (const wfExpr of buildOperatorList(true)) { - const wfArg = Object.assign({window: {documents: [-1, 1]}}, wfExpr); - createView( - [{ - $setWindowFields: - {partitionBy: "$partitionKey", sortBy: {sortField: 1}, output: {foo: wfArg}} - }], - [ErrorCodes.FailedToParse, ErrorCodes.QueryFeatureNotAllowed]); - } - } - - // Standalone. - const dbPath = MongoRunner.dataPath + "/exact_top_n_upgrade_downgrade"; - let conn = MongoRunner.runMongod({dbpath: dbPath, binVersion: downgradeVersion}); - let testDB = conn.getDB(dbName); - let coll = testDB[collName]; - assert.commandWorked(coll.insert({})); - - // This shouldn't pass in 'downgradeVersion'. - assertExpectedBehavior(false, testDB, coll); - - // Upgrade the standalone to the latest binVersion; this still shouldn't pass. - MongoRunner.stopMongod(conn); - conn = MongoRunner.runMongod( - {binVersion: "latest", restart: conn, cleanData: false, dbpath: dbPath}); - testDB = conn.getDB(dbName); - let adminDB = conn.getDB("admin"); - coll = testDB[collName]; - checkFCV(adminDB, downgradeVersion); - assertExpectedBehavior(false, testDB, coll); - - // Set the FCV to 'latestFCV'; this should now pass. - checkFCV(adminDB, downgradeVersion); - assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: latestFCV})); - checkFCV(adminDB, latestFCV); - assertExpectedBehavior(true, testDB, coll); - MongoRunner.stopMongod(conn); - - // Replica set. - - // Set up a replica-set in 'downgradeVersion'. - const rst = new ReplSetTest({nodes: 2, nodeOptions: {binVersion: downgradeVersion}}); - rst.startSet(); - rst.initiate(); - - let primary = rst.getPrimary(); - testDB = primary.getDB(dbName); - coll = testDB[collName]; - assert.commandWorked(coll.insert({})); - - // Shouldn't pass in 'downgradeVersion'. - assertExpectedBehavior(false, testDB); - - // Upgrade the replica set. - rst.upgradeSet({binVersion: "latest"}); - - // Verify that all nodes are in the latest version. - for (const node of rst.nodes) { - assert.binVersion(node, "latest"); - } - - rst.awaitNodesAgreeOnPrimary(); - primary = rst.getPrimary(); - testDB = primary.getDB(dbName); - - // Despite the upgrade, the test shouldn't pass because the FCV has not been explicitly set. - assertExpectedBehavior(false, testDB); - - // Set the FCV; the test should now pass. - primary = rst.getPrimary(); - adminDB = primary.getDB("admin"); - checkFCV(adminDB, downgradeVersion); - assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); - checkFCV(adminDB, latestFCV); - testDB = primary.getDB(dbName); - assertExpectedBehavior(true, testDB); - rst.stopSet(); - - // Sharded cluster. - const st = new ShardingTest({ - shards: 2, - rs: {nodes: 2, binVersion: downgradeVersion}, - other: { - mongosOptions: {binVersion: downgradeVersion}, - configOptions: {binVersion: downgradeVersion} - } - }); - - testDB = st.s.getDB(dbName); - coll = testDB[collName]; - assert.commandWorked(coll.insert({})); - - // The test shouldn't pass in 'downgradeVersion'. - adminDB = st.s.getDB("admin"); - checkFCV(adminDB, downgradeVersion); - assertExpectedBehavior(false, testDB); - - // Upgrade the cluster. - st.upgradeCluster("latest", {waitUntilStable: true}); - testDB = st.s.getDB(dbName); - - // Despite the upgrade, the test shouldn't pass because the FCV has not been explicitly set. - adminDB = st.s.getDB("admin"); - checkFCV(adminDB, downgradeVersion); - assertExpectedBehavior(false, testDB); - - // Set the FCV; the test should now pass. - assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); - checkFCV(adminDB, latestFCV); - assertExpectedBehavior(true, testDB); - st.stop(); -} - -runFeatureFlagMultiversionTest("featureFlagExactTopNAccumulator", runTest); -})(); diff --git a/jstests/multiVersion/query_feature_flags.js b/jstests/multiVersion/query_feature_flags.js new file mode 100644 index 00000000000..cd7e4580eaf --- /dev/null +++ b/jstests/multiVersion/query_feature_flags.js @@ -0,0 +1,248 @@ +/** + * Tests that various query-specific feature flags work correctly. + * + * TODO SERVER-61851 Delete this test once we branch for 6.0. + * + * @tags: [requires_replication, requires_sharding] + */ + +(function() { +"use strict"; + +load("jstests/multiVersion/libs/verify_versions.js"); +load("jstests/multiVersion/libs/multi_rs.js"); // For upgradeSecondaries and upgradeSet. +load("jstests/multiVersion/libs/multi_cluster.js"); // For upgradeCluster. + +function createView(pipeline, expectedErrorCodes, expectedToPass, db, collName) { + const viewName = "testView"; + jsTestLog("Attempting to create view: " + tojson(pipeline)); + if (expectedToPass) { + assert.commandWorked(db.createView(viewName, collName, pipeline)); + assert(db[viewName].drop()); + } else { + assert.commandFailedWithCode(db.createView(viewName, collName, pipeline), + expectedErrorCodes); + } +} + +function createValidator(aggExpr, expectedErrorCodes, expectedToPass, db, collName) { + const collMod = {collMod: collName, validator: {$expr: aggExpr}}; + jsTestLog("Attempting to create validator: " + tojson(collMod)); + if (expectedToPass) { + assert.commandWorked(db.runCommand(collMod)); + } else { + assert.commandFailedWithCode(db.runCommand(collMod), expectedErrorCodes); + } +} + +const assertExpectedBehaviorSortArray = (expectedToPass, db, collName) => { + const aggExpr = {$sortArray: {input: "$a", sortBy: 1}}; + + createView([{$project: {output: aggExpr}}], + [31325, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); + createValidator(aggExpr, + [ErrorCodes.InvalidPipelineOperator, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); +}; + +const assertExpectedBehaviorTopN = (expectedToPass, db, collName) => { + function getOperators(needTopBottom) { + let obj = {needsInputAndN: ["$firstN", "$lastN", "$minN", "$maxN"]}; + if (needTopBottom) { + Object.assign( + obj, {needsOutputAndN: ["$topN", "$bottomN"], needsOutput: ["$top", "$bottom"]}); + } + return obj; + } + + function buildOperatorList(needAccumulators) { + let operatorList = []; + for (const [category, opNames] of Object.entries(getOperators(needAccumulators))) { + for (const opName of opNames) { + let spec = {}; + if (category === "needsInputAndN") { + Object.assign(spec, {input: "$a", n: 10}); + } else if (category === "needsOutputAndN") { + Object.assign(spec, {output: "$a", n: 10, sortBy: {b: 1}}); + } else if (category === "needsOutput") { + Object.assign(spec, {output: "$a", sortBy: {b: 1}}); + } + operatorList.push({[opName]: spec}); + } + } + return operatorList; + } + + // Accumulators. + for (const accSpec of buildOperatorList(true)) { + createView([{$group: {_id: "$groupKey", output: accSpec}}], + [15952, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); + } + + // Aggregation expressions. + for (const aggExpr of buildOperatorList(false)) { + createView([{$project: {output: aggExpr}}], + [31325, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); + createValidator(aggExpr, + [ErrorCodes.InvalidPipelineOperator, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); + } + + // Window functions. + for (const wfExpr of buildOperatorList(true)) { + const wfArg = Object.assign({window: {documents: [-1, 1]}}, wfExpr); + createView( + [{ + $setWindowFields: + {partitionBy: "$partitionKey", sortBy: {sortField: 1}, output: {foo: wfArg}} + }], + [ErrorCodes.FailedToParse, ErrorCodes.QueryFeatureNotAllowed], + expectedToPass, + db, + collName); + } +}; + +const testFeatureFlagConfigs = [ + // Exact Top-N. + { + name: 'featureFlagExactTopNAccumulator', + collName: 'exactTopNUpgradeDowngrade', + dataPath: 'exact_top_n_upgrade_downgrade', + assertExpectedBehavior: assertExpectedBehaviorTopN + }, + // $sortArray. + { + name: 'featureFlagSortArray', + collName: 'sortArrayUpgradeDowngrade', + dataPath: 'sort_array_upgrade_downgrade', + assertExpectedBehavior: assertExpectedBehaviorSortArray + } +]; + +function makeQueryFeatureFlagTest(testFeatureFlagConfig) { + return function runTest(downgradeVersion) { + const dbName = "db"; + const collName = testFeatureFlagConfig.collName; + const assertExpectedBehavior = testFeatureFlagConfig.assertExpectedBehavior; + + // Standalone. + const dbPath = MongoRunner.dataPath + testFeatureFlagConfig.dbPath; + let conn = MongoRunner.runMongod({dbpath: dbPath, binVersion: downgradeVersion}); + let testDB = conn.getDB(dbName); + let coll = testDB[collName]; + assert.commandWorked(coll.insert({})); + + // This shouldn't pass in 'downgradeVersion'. + assertExpectedBehavior(false, testDB, collName); + + // Upgrade the standalone to the latest binVersion; this still shouldn't pass. + MongoRunner.stopMongod(conn); + conn = MongoRunner.runMongod( + {binVersion: "latest", restart: conn, cleanData: false, dbpath: dbPath}); + testDB = conn.getDB(dbName); + let adminDB = conn.getDB("admin"); + coll = testDB[collName]; + checkFCV(adminDB, downgradeVersion); + assertExpectedBehavior(false, testDB, collName); + + // Set the FCV to 'latestFCV'; this should now pass. + checkFCV(adminDB, downgradeVersion); + assert.commandWorked(conn.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + checkFCV(adminDB, latestFCV); + assertExpectedBehavior(true, testDB, collName); + MongoRunner.stopMongod(conn); + + // Replica set. + + // Set up a replica-set in 'downgradeVersion'. + const rst = new ReplSetTest({nodes: 2, nodeOptions: {binVersion: downgradeVersion}}); + rst.startSet(); + rst.initiate(); + + let primary = rst.getPrimary(); + testDB = primary.getDB(dbName); + coll = testDB[collName]; + assert.commandWorked(coll.insert({})); + + // Shouldn't pass in 'downgradeVersion'. + assertExpectedBehavior(false, testDB, collName); + + // Upgrade the replica set. + rst.upgradeSet({binVersion: "latest"}); + + // Verify that all nodes are in the latest version. + for (const node of rst.nodes) { + assert.binVersion(node, "latest"); + } + + rst.awaitNodesAgreeOnPrimary(); + primary = rst.getPrimary(); + testDB = primary.getDB(dbName); + + // Despite the upgrade, the test shouldn't pass because the FCV has not been explicitly set. + assertExpectedBehavior(false, testDB, collName); + + // Set the FCV; the test should now pass. + primary = rst.getPrimary(); + adminDB = primary.getDB("admin"); + checkFCV(adminDB, downgradeVersion); + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); + checkFCV(adminDB, latestFCV); + testDB = primary.getDB(dbName); + assertExpectedBehavior(true, testDB, collName); + rst.stopSet(); + + // Sharded cluster. + const st = new ShardingTest({ + shards: 2, + rs: {nodes: 2, binVersion: downgradeVersion}, + other: { + mongosOptions: {binVersion: downgradeVersion}, + configOptions: {binVersion: downgradeVersion} + } + }); + + testDB = st.s.getDB(dbName); + coll = testDB[collName]; + assert.commandWorked(coll.insert({})); + + // The test shouldn't pass in 'downgradeVersion'. + adminDB = st.s.getDB("admin"); + checkFCV(adminDB, downgradeVersion); + assertExpectedBehavior(false, testDB, collName); + + // Upgrade the cluster. + st.upgradeCluster("latest", {waitUntilStable: true}); + testDB = st.s.getDB(dbName); + + // Despite the upgrade, the test shouldn't pass because the FCV has not been explicitly set. + adminDB = st.s.getDB("admin"); + checkFCV(adminDB, downgradeVersion); + assertExpectedBehavior(false, testDB, collName); + + // Set the FCV; the test should now pass. + assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: latestFCV})); + checkFCV(adminDB, latestFCV); + assertExpectedBehavior(true, testDB, collName); + st.stop(); + }; +} + +for (const config of testFeatureFlagConfigs) { + runFeatureFlagMultiversionTest(config.name, makeQueryFeatureFlagTest(config)); +} +})(); |