summaryrefslogtreecommitdiff
path: root/jstests/libs
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-14 14:26:38 -0300
commit294bc6ecabf14c09c9bc8644704921dcf97cb44e (patch)
tree279b1e0bab53901a1647ac63c1c724f0f789a663 /jstests/libs
parent70be7c27a251621187a1de533462ae2bb1e3bd39 (diff)
parent1e917fd798aa25b7066d4b414b51184f13d5a092 (diff)
Update upstream source from tag 'upstream/6.0.10'debian/6.0.10-1
Update to upstream version '6.0.10' with Debian dir 2d176fa254eee97b139f712fec5709641335a8c3
Diffstat (limited to 'jstests/libs')
-rw-r--r--jstests/libs/analyze_plan.js15
-rw-r--r--jstests/libs/api_version_helpers.js10
-rw-r--r--jstests/libs/change_stream_rewrite_util.js10
-rw-r--r--jstests/libs/cluster_server_parameter_utils.js3
-rw-r--r--jstests/libs/clustered_collections/clustered_capped_utils.js12
-rw-r--r--jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js19
-rw-r--r--jstests/libs/clustered_collections/clustered_collection_util.js10
-rw-r--r--jstests/libs/command_line/test_parsed_options.js4
-rw-r--r--jstests/libs/conn_pool_helpers.js64
-rw-r--r--jstests/libs/index_catalog_helpers.js85
-rw-r--r--jstests/libs/os_helpers.js211
-rw-r--r--jstests/libs/override_methods/config_fuzzer_incompatible_commands.js34
-rw-r--r--jstests/libs/override_methods/implicitly_shard_accessed_collections.js5
-rw-r--r--jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js2
-rw-r--r--jstests/libs/override_methods/network_error_and_txn_override.js12
-rw-r--r--jstests/libs/parallelTester.js1
-rw-r--r--jstests/libs/ttl_util.js40
17 files changed, 500 insertions, 37 deletions
diff --git a/jstests/libs/analyze_plan.js b/jstests/libs/analyze_plan.js
index dcfb3f11221..9ae5e62ba65 100644
--- a/jstests/libs/analyze_plan.js
+++ b/jstests/libs/analyze_plan.js
@@ -526,9 +526,18 @@ function getPlanCacheKeyFromExplain(explainRes, db) {
* Helper to run a explain on the given query shape and get the "planCacheKey" from the explain
* result.
*/
-function getPlanCacheKeyFromShape({query = {}, projection = {}, sort = {}, collection, db}) {
- const explainRes =
- assert.commandWorked(collection.explain().find(query, projection).sort(sort).finish());
+function getPlanCacheKeyFromShape({
+ query = {},
+ projection = {},
+ sort = {},
+ collation = {
+ locale: "simple"
+ },
+ collection,
+ db
+}) {
+ const explainRes = assert.commandWorked(
+ collection.explain().find(query, projection).collation(collation).sort(sort).finish());
return getPlanCacheKeyFromExplain(explainRes, db);
}
diff --git a/jstests/libs/api_version_helpers.js b/jstests/libs/api_version_helpers.js
index 363d89e6d32..c4b0529769a 100644
--- a/jstests/libs/api_version_helpers.js
+++ b/jstests/libs/api_version_helpers.js
@@ -51,9 +51,9 @@ var APIVersionHelpers = (function() {
* Asserts that the given pipeline cannot be used to define a view when apiStrict is set to true
* and apiVersion is "1" on the create command.
*/
- function assertViewFailsWithAPIStrict(pipeline, collName) {
+ function assertViewFailsWithAPIStrict(pipeline, viewName, collName) {
assert.commandFailedWithCode(db.runCommand({
- create: 'new_50_feature_view',
+ create: viewName,
viewOn: collName,
pipeline: pipeline,
apiStrict: true,
@@ -67,16 +67,16 @@ var APIVersionHelpers = (function() {
* Asserts that the given pipeline can be used to define a view when apiStrict is set to true
* and apiVersion is "1" on the create command.
*/
- function assertViewSucceedsWithAPIStrict(pipeline, collName) {
+ function assertViewSucceedsWithAPIStrict(pipeline, viewName, collName) {
assert.commandWorked(db.runCommand({
- create: 'new_50_feature_view',
+ create: viewName,
viewOn: collName,
pipeline: pipeline,
apiStrict: true,
apiVersion: "1"
}));
- assert.commandWorked(db.runCommand({drop: 'new_50_feature_view'}));
+ assert.commandWorked(db.runCommand({drop: viewName}));
}
return {
diff --git a/jstests/libs/change_stream_rewrite_util.js b/jstests/libs/change_stream_rewrite_util.js
index ebbb0937222..918873c17bc 100644
--- a/jstests/libs/change_stream_rewrite_util.js
+++ b/jstests/libs/change_stream_rewrite_util.js
@@ -138,7 +138,10 @@ function assertNumMatchingOplogEventsForShard(stats, shardName, expectedTotalRet
assert(stats.shards.hasOwnProperty(shardName), stats);
assert.eq(Object.keys(stats.shards[shardName].stages[0])[0], "$cursor", stats);
const executionStats = stats.shards[shardName].stages[0].$cursor.executionStats;
- assert.eq(executionStats.nReturned, expectedTotalReturned, executionStats);
+ assert.eq(executionStats.nReturned,
+ expectedTotalReturned,
+ () => `Expected ${expectedTotalReturned} events on shard ${shardName} but got ` +
+ `${executionStats.nReturned}. Execution stats:\n${tojson(executionStats)}`);
}
// Returns a newly created sharded collection sharded by caller provided shard key.
@@ -180,7 +183,10 @@ function verifyChangeStreamOnWholeCluster(
eventIdentifierList.forEach(eventIdentifier => {
assert.soon(() => cursor.hasNext(), {op: op, eventIdentifier: eventIdentifier});
const event = cursor.next();
- assert.eq(event.operationType, op, event);
+ assert.eq(event.operationType,
+ op,
+ () => `Expected "${op}" but got "${event.operationType}". Full event: ` +
+ `${tojson(event)}`);
if (op == "dropDatabase") {
assert.eq(event.ns.db, eventIdentifier, event);
diff --git a/jstests/libs/cluster_server_parameter_utils.js b/jstests/libs/cluster_server_parameter_utils.js
index f50b487c40a..2b6c93a5d54 100644
--- a/jstests/libs/cluster_server_parameter_utils.js
+++ b/jstests/libs/cluster_server_parameter_utils.js
@@ -116,6 +116,9 @@ function setupSharded(st) {
shards.forEach(function(shard) {
setupReplicaSet(shard);
});
+
+ // Wait for FCV to fully replicate on all shards before performing test commands.
+ st.awaitReplicationOnShards();
}
// Upserts config.clusterParameters document with w:majority via setClusterParameter.
diff --git a/jstests/libs/clustered_collections/clustered_capped_utils.js b/jstests/libs/clustered_collections/clustered_capped_utils.js
index 8e6ae836223..e9c136f57fd 100644
--- a/jstests/libs/clustered_collections/clustered_capped_utils.js
+++ b/jstests/libs/clustered_collections/clustered_capped_utils.js
@@ -1,3 +1,5 @@
+load("jstests/libs/ttl_util.js");
+
var ClusteredCappedUtils = class {
// Validate TTL-based deletion on a clustered, capped collection.
static testClusteredCappedCollectionWithTTL(db, collName, clusterKeyField) {
@@ -39,7 +41,8 @@ var ClusteredCappedUtils = class {
}
assert.commandWorked(coll.insertMany(docs, {ordered: true}));
- ClusteredCollectionUtil.waitForTTL(db);
+ // This test runs with default read concern 'local'.
+ TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/false);
// Only the recent documents survived.
assert.eq(coll.find().itcount(), batchSize);
@@ -140,7 +143,7 @@ var ClusteredCappedUtils = class {
// TTL delete the two old documents.
assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true}));
- ClusteredCollectionUtil.waitForTTL(db);
+ TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/isReplicated);
assert.eq(2, db.getCollection(collName).find().itcount());
// Confirm that the tailable getMore can resume from where it was, since the document the
@@ -209,7 +212,7 @@ var ClusteredCappedUtils = class {
// TTL delete the two old documents, while the tailable cursor is still on the first one.
assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true}));
- ClusteredCollectionUtil.waitForTTL(db);
+ TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/isReplicated);
assert.eq(1, db.getCollection(collName).find().itcount());
// Confirm that the tailable cursor returns CappedPositionLost, as the document it was
@@ -292,7 +295,8 @@ var ClusteredCappedUtils = class {
// Expire the document.
assert.commandWorked(db.adminCommand({setParameter: 1, ttlMonitorEnabled: true}));
- ClusteredCollectionUtil.waitForTTL(db);
+ // No need to wait for majority commit, as default 'local' read concern is used.
+ TTLUtil.waitForPass(db, /*waitForMajorityCommit=*/false);
assert.eq(0, db.getCollection(collName).find().itcount());
// The TTL deletion has been replicated to the oplog.
diff --git a/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js b/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js
index b81717c7552..f9495c913c5 100644
--- a/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js
+++ b/jstests/libs/clustered_collections/clustered_collection_bounded_scan_common.js
@@ -94,6 +94,23 @@ const testClusteredCollectionBoundedScan = function(coll, clusterKey) {
assert.eq(expectedNReturned, expl.executionStats.executionStages.nReturned);
assert.eq(expectedDocsExamined, expl.executionStats.executionStages.docsExamined);
}
+ function testIn() {
+ initAndPopulate(coll, clusterKey);
+
+ const expl = assert.commandWorked(coll.getDB().runCommand({
+ explain: {find: coll.getName(), filter: {[clusterKeyFieldName]: {$in: [10, 20, 30]}}},
+ verbosity: "executionStats"
+ }));
+
+ assert(getPlanStage(expl, "CLUSTERED_IXSCAN"));
+ assert.eq(10, getPlanStage(expl, "CLUSTERED_IXSCAN").minRecord);
+ assert.eq(30, getPlanStage(expl, "CLUSTERED_IXSCAN").maxRecord);
+
+ assert.eq(3, expl.executionStats.executionStages.nReturned);
+ // The range scanned is 21 documents + 1 extra document by design - additional cursor
+ // 'next' beyond the range.
+ assert.eq(22, expl.executionStats.executionStages.docsExamined);
+ }
function testNonClusterKeyScan() {
initAndPopulate(coll, clusterKey);
@@ -128,6 +145,8 @@ const testClusteredCollectionBoundedScan = function(coll, clusterKey) {
testRange("$gte", 20, "$lt", 40, 20, 22);
testRange("$gt", 20, "$lte", 40, 20, 22);
testRange("$gte", 20, "$lte", 40, 21, 22);
+ testIn();
+
testNonClusterKeyScan();
}
diff --git a/jstests/libs/clustered_collections/clustered_collection_util.js b/jstests/libs/clustered_collections/clustered_collection_util.js
index ad6c0111c8a..c63b5ce56e9 100644
--- a/jstests/libs/clustered_collections/clustered_collection_util.js
+++ b/jstests/libs/clustered_collections/clustered_collection_util.js
@@ -196,14 +196,4 @@ var ClusteredCollectionUtil = class {
assert.eq(1, coll.find({[clusterKey]: NumberLong("42")}).itcount());
coll.drop();
}
-
- static waitForTTL(db) {
- // The 'ttl.passes' metric is incremented when the TTL monitor starts processing the
- // indexes, so we wait for it to be incremented twice to know that the TTL monitor finished
- // processing the indexes at least once.
- const ttlPasses = db.serverStatus().metrics.ttl.passes;
- assert.soon(function() {
- return db.serverStatus().metrics.ttl.passes > ttlPasses + 1;
- });
- }
};
diff --git a/jstests/libs/command_line/test_parsed_options.js b/jstests/libs/command_line/test_parsed_options.js
index 5c32f3a8774..e4400952760 100644
--- a/jstests/libs/command_line/test_parsed_options.js
+++ b/jstests/libs/command_line/test_parsed_options.js
@@ -76,6 +76,8 @@ function testGetCmdLineOptsMongod(mongoRunnerConfig, expectedResult) {
typeof expectedResult.parsed.storage.dbPath === "undefined") {
delete getCmdLineOptsExpected.parsed.storage.dbPath;
}
+ // Delete backtraceLogFile parameter, since we are generating unique value every time
+ delete getCmdLineOptsExpected.parsed.setParameter.backtraceLogFile;
// Merge with the result that we expect
expectedResult = mergeOptions(getCmdLineOptsExpected, expectedResult);
@@ -108,6 +110,8 @@ function testGetCmdLineOptsMongod(mongoRunnerConfig, expectedResult) {
typeof expectedResult.parsed.storage.dbPath === "undefined") {
delete getCmdLineOptsResult.parsed.storage.dbPath;
}
+ // Delete backtraceLogFile parameter, since we are generating unique value every time
+ delete getCmdLineOptsResult.parsed.setParameter.backtraceLogFile;
// Make sure the options are equal to what we expect
assert.docEq(getCmdLineOptsResult.parsed, expectedResult.parsed);
diff --git a/jstests/libs/conn_pool_helpers.js b/jstests/libs/conn_pool_helpers.js
new file mode 100644
index 00000000000..9beb49da1d1
--- /dev/null
+++ b/jstests/libs/conn_pool_helpers.js
@@ -0,0 +1,64 @@
+load("jstests/libs/parallelTester.js");
+
+function configureReplSetFailpoint(st, kDbName, failpoint, modeValue) {
+ st.rs0.nodes.forEach(function(node) {
+ assert.commandWorked(node.getDB("admin").runCommand({
+ configureFailPoint: failpoint,
+ mode: modeValue,
+ data: {
+ shouldCheckForInterrupt: true,
+ nss: kDbName + ".test",
+ },
+ }));
+ });
+}
+
+function launchFinds(mongos, threads, {times, readPref, shouldFail}) {
+ jsTestLog("Starting " + times + " connections");
+ for (var i = 0; i < times; i++) {
+ var thread = new Thread(function(connStr, readPref, dbName, shouldFail) {
+ var client = new Mongo(connStr);
+ const ret = client.getDB(dbName).runCommand(
+ {find: "test", limit: 1, "$readPreference": {mode: readPref}});
+
+ if (shouldFail) {
+ assert.commandFailed(ret);
+ } else {
+ assert.commandWorked(ret);
+ }
+ }, mongos.host, readPref, 'test', shouldFail);
+ thread.start();
+ threads.push(thread);
+ }
+}
+
+function assertHasConnPoolStats(mongos, allHosts, args, checkNum, connPoolStatsCmd = undefined) {
+ checkNum++;
+ jsTestLog("Check #" + checkNum + ": " + tojson(args));
+ let {ready = 0, pending = 0, active = 0, hosts = allHosts, isAbsent, checkStatsFunc} = args;
+ checkStatsFunc = checkStatsFunc ? checkStatsFunc : function(stats) {
+ return stats.available == ready && stats.refreshing == pending &&
+ (stats.inUse + stats.leased) == active;
+ };
+
+ function checkStats(res, host) {
+ let stats = res.hosts[host];
+ if (!stats) {
+ jsTestLog("Connection stats for " + host + " are absent");
+ return isAbsent;
+ }
+
+ jsTestLog("Connection stats for " + host + ": " + tojson(stats));
+ return checkStatsFunc(stats);
+ }
+
+ function checkAllStats() {
+ let cmdName = connPoolStatsCmd ? connPoolStatsCmd : "connPoolStats";
+ let res = mongos.adminCommand({[cmdName]: 1});
+ return hosts.map(host => checkStats(res, host)).every(x => x);
+ }
+
+ assert.soon(checkAllStats, "Check #" + checkNum + " failed", 10000);
+ jsTestLog("Check #" + checkNum + " successful");
+ return checkNum;
+}
diff --git a/jstests/libs/index_catalog_helpers.js b/jstests/libs/index_catalog_helpers.js
new file mode 100644
index 00000000000..a61267fbbbd
--- /dev/null
+++ b/jstests/libs/index_catalog_helpers.js
@@ -0,0 +1,85 @@
+"use strict";
+
+/**
+ * Helper functions that help test things to do with the index catalog.
+ */
+var IndexCatalogHelpers = (function() {
+ /**
+ * Returns the index specification with the name 'indexName' if it is present in the
+ * 'indexSpecs' array, and returns null otherwise.
+ */
+ function getIndexSpecByName(indexSpecs, indexName) {
+ if (typeof indexName !== "string") {
+ throw new Error("'indexName' parameter must be a string, but got " + tojson(indexName));
+ }
+
+ const found = indexSpecs.filter(spec => spec.name === indexName);
+
+ if (found.length > 1) {
+ throw new Error("Found multiple indexes with name '" + indexName +
+ "': " + tojson(indexSpecs));
+ }
+ return (found.length === 1) ? found[0] : null;
+ }
+
+ /**
+ * Returns the index specification with the key pattern 'keyPattern' and the collation
+ * 'collation' if it is present in the 'indexSpecs' array, and returns null otherwise.
+ *
+ * The 'collation' parameter is optional and is only required to be specified when multiple
+ * indexes with the same key pattern exist.
+ */
+ function getIndexSpecByKeyPattern(indexSpecs, keyPattern, collation) {
+ const collationWasSpecified = arguments.length >= 3;
+ const foundByKeyPattern = indexSpecs.filter(spec => {
+ return bsonWoCompare(spec.key, keyPattern) === 0;
+ });
+
+ if (!collationWasSpecified) {
+ if (foundByKeyPattern.length > 1) {
+ throw new Error(
+ "Found multiple indexes with key pattern " + tojson(keyPattern) +
+ " and 'collation' parameter was not specified: " + tojson(indexSpecs));
+ }
+ return (foundByKeyPattern.length === 1) ? foundByKeyPattern[0] : null;
+ }
+
+ const foundByKeyPatternAndCollation = foundByKeyPattern.filter(spec => {
+ if (collation.locale === "simple") {
+ // The simple collation is not explicitly stored in the index catalog, so we expect
+ // the "collation" field to be absent.
+ return !spec.hasOwnProperty("collation");
+ }
+ return bsonWoCompare(spec.collation, collation) === 0;
+ });
+
+ if (foundByKeyPatternAndCollation.length > 1) {
+ throw new Error("Found multiple indexes with key pattern" + tojson(keyPattern) +
+ " and collation " + tojson(collation) + ": " + tojson(indexSpecs));
+ }
+ return (foundByKeyPatternAndCollation.length === 1) ? foundByKeyPatternAndCollation[0]
+ : null;
+ }
+
+ function createSingleIndex(coll, key, parameters) {
+ return coll.getDB().runCommand(
+ {createIndexes: coll.getName(), indexes: [Object.assign({key: key}, parameters)]});
+ }
+
+ function createIndexAndVerifyWithDrop(coll, key, parameters) {
+ coll.dropIndexes();
+ assert.commandWorked(createSingleIndex(coll, key, parameters));
+ assert.neq(
+ null,
+ getIndexSpecByName(coll.getIndexes(), parameters.name),
+ () =>
+ `Could not find index with name ${parameters.name}: ${tojson(coll.getIndexes())}`);
+ }
+
+ return {
+ findByName: getIndexSpecByName,
+ findByKeyPattern: getIndexSpecByKeyPattern,
+ createSingleIndex: createSingleIndex,
+ createIndexAndVerifyWithDrop: createIndexAndVerifyWithDrop,
+ };
+})();
diff --git a/jstests/libs/os_helpers.js b/jstests/libs/os_helpers.js
new file mode 100644
index 00000000000..f7cafa2d898
--- /dev/null
+++ b/jstests/libs/os_helpers.js
@@ -0,0 +1,211 @@
+/**
+ * Test helpers for identifying OS
+ */
+
+function isLinux() {
+ return getBuildInfo().buildEnvironment.target_os == "linux";
+}
+
+function isMacOS() {
+ return getBuildInfo().buildEnvironment.target_os == "macOS";
+}
+
+// See "man 5 os-release" for documentation
+function readOsRelease() {
+ try {
+ const os_release = cat("/etc/os-release");
+
+ let lines = os_release.split("\n");
+
+ let tags = {};
+
+ for (let line of lines) {
+ let vp = line.replaceAll("\"", "").split("=");
+ tags[vp[0]] = vp[1];
+ }
+
+ return tags;
+ } catch (ignored) {
+ // ignore
+ }
+
+ assert(!isLinux(), "Linux hosts should always have /etc/os-release.");
+
+ return {};
+}
+
+/**
+ * Check if Linux OS is given identifier. Identifiers are always lower case strings.
+ *
+ * @param {string} distro ID of the distro in os-release
+ * @returns
+ */
+function isDistro(distro) {
+ let tags = readOsRelease();
+ return tags.hasOwnProperty("ID") && tags["ID"] === distro;
+}
+
+/**
+ * Check if Linux OS is given identifier and specific version. Do not use for matching major
+ * versions like RHEL 8, isRHELMajorVerison.
+ *
+ * @param {string} distro ID of the distro in os-release
+ * @returns
+ */
+function isDistroVersion(distro, version) {
+ let tags = readOsRelease();
+ return tags.hasOwnProperty("ID") && tags["ID"] === distro &&
+ tags.hasOwnProperty("VERSION_ID") && tags["VERSION_ID"] === version;
+}
+
+/**
+ * Is it RHEL and is it 7, 8 or 9?
+ * @param {string} majorVersion
+ * @returns True if majorVersion = 8 and version is 8.1, 8.2 etc.
+ */
+function isRHELMajorVerison(majorVersion) {
+ let tags = readOsRelease();
+ return tags.hasOwnProperty("ID") && tags["ID"] === "rhel" &&
+ tags.hasOwnProperty("VERSION_ID") && tags["VERSION_ID"].startsWith(majorVersion);
+}
+
+/**
+ * Example
+NAME="Red Hat Enterprise Linux"
+VERSION="8.7 (Ootpa)"
+ID="rhel"
+ID_LIKE="fedora"
+VERSION_ID="8.7"
+PLATFORM_ID="platform:el8"
+PRETTY_NAME="Red Hat Enterprise Linux 8.7 (Ootpa)"
+ANSI_COLOR="0;31"
+CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos"
+HOME_URL="https://www.redhat.com/"
+DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/"
+BUG_REPORT_URL="https://bugzilla.redhat.com/"
+
+REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
+REDHAT_BUGZILLA_PRODUCT_VERSION=8.7
+REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
+REDHAT_SUPPORT_PRODUCT_VERSION="8.7"
+ */
+function isRHEL8() {
+ // RHEL 8 disables TLS 1.0 and TLS 1.1 as part their default crypto policy
+ // We skip tests on RHEL 8 that require these versions as a result.
+ return isRHELMajorVerison("8");
+}
+
+function isSUSE15SP1() {
+ if (_isWindows()) {
+ return false;
+ }
+
+ // SUSE 15 SP1 FIPS module does not work. SP2 does work.
+ // The FIPS code returns FIPS_R_IN_ERROR_STATE in what is likely a race condition
+ // since it only happens in sharded clusters.
+ const grep_result = runProgram('grep', '15-SP1', '/etc/os-release');
+ if (grep_result == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+function isUbuntu() {
+ // Ubuntu 18.04 and later compiles openldap against gnutls which does not
+ // support SHA1 signed certificates. ldaptest.10gen.cc uses a SHA1 cert.
+ return isDistro("ubuntu");
+}
+
+/**
+ * Example:
+NAME="Ubuntu"
+VERSION="18.04.6 LTS (Bionic Beaver)"
+ID=ubuntu
+ID_LIKE=debian
+PRETTY_NAME="Ubuntu 18.04.6 LTS"
+VERSION_ID="18.04"
+HOME_URL="https://www.ubuntu.com/"
+SUPPORT_URL="https://help.ubuntu.com/"
+BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
+PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
+VERSION_CODENAME=bionic
+UBUNTU_CODENAME=bionic
+ */
+function isUbuntu1804() {
+ // Ubuntu 18.04's TLS 1.3 implementation has an issue with OCSP stapling. We have disabled
+ // stapling on this build variant, so we need to ensure that tests that require stapling
+ // do not run on this machine.
+ return isDistroVersion("ubuntu", "18.04");
+}
+
+function isUbuntu2004() {
+ // Ubuntu 20.04 disables TLS 1.0 and TLS 1.1 as part their default crypto policy
+ // We skip tests on Ubuntu 20.04 that require these versions as a result.
+ return isDistroVersion("ubuntu", "20.04");
+}
+
+/**
+ * Example:
+PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
+NAME="Debian GNU/Linux"
+VERSION_ID="12"
+VERSION="12 (bookworm)"
+VERSION_CODENAME=bookworm
+ID=debian
+HOME_URL="https://www.debian.org/"
+SUPPORT_URL="https://www.debian.org/support"
+BUG_REPORT_URL="https://bugs.debian.org/"
+ */
+function isDebian() {
+ return isDistro("debian");
+}
+
+/**
+ * Example:
+NAME="Fedora Linux"
+VERSION="38 (Workstation Edition)"
+ID=fedora
+VERSION_ID=38
+VERSION_CODENAME=""
+PLATFORM_ID="platform:f38"
+PRETTY_NAME="Fedora Linux 38 (Workstation Edition)"
+ANSI_COLOR="0;38;2;60;110;180"
+LOGO=fedora-logo-icon
+CPE_NAME="cpe:/o:fedoraproject:fedora:38"
+DEFAULT_HOSTNAME="fedora"
+HOME_URL="https://fedoraproject.org/"
+DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f38/system-administrators-guide/"
+SUPPORT_URL="https://ask.fedoraproject.org/"
+BUG_REPORT_URL="https://bugzilla.redhat.com/"
+REDHAT_BUGZILLA_PRODUCT="Fedora"
+REDHAT_BUGZILLA_PRODUCT_VERSION=38
+REDHAT_SUPPORT_PRODUCT="Fedora"
+REDHAT_SUPPORT_PRODUCT_VERSION=38
+SUPPORT_END=2024-05-14
+VARIANT="Workstation Edition"
+VARIANT_ID=workstation
+ */
+function isFedora() {
+ return isDistro("fedora");
+}
+
+/**
+ * Note: Amazon 2022 was never released for production. It became Amazon 2023.
+ *
+ * Example:
+NAME="Amazon Linux"
+VERSION="2022"
+ID="amzn"
+ID_LIKE="fedora"
+VERSION_ID="2022"
+PLATFORM_ID="platform:al2022"
+PRETTY_NAME="Amazon Linux 2022"
+ANSI_COLOR="0;33"
+CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2022"
+HOME_URL="https://aws.amazon.com/linux/"
+BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2022"
+*/
+function isAmazon2023() {
+ return isDistroVersion("amzn", "2022") || isDistroVersion("amzn", "2023");
+}
diff --git a/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js b/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js
new file mode 100644
index 00000000000..b20e5accfe9
--- /dev/null
+++ b/jstests/libs/override_methods/config_fuzzer_incompatible_commands.js
@@ -0,0 +1,34 @@
+/**
+ * Overrides commands that are incompatible to run when the config fuzzer is enabled.
+ */
+(function() {
+"use strict";
+
+load("jstests/libs/override_methods/override_helpers.js");
+
+function runCommandOverride(conn, dbName, commandName, commandObj, func, makeFuncArgs) {
+ if (commandName == "compact") {
+ // A single compact can perform up to 10-20 checkpoints - which can cause tests to time out
+ // when mongod configurations stress the server.
+ throw new Error(
+ "Cowardly refusing to run test that uses command 'compact' with the config fuzzer enabled. " +
+ tojson(commandObj));
+ }
+
+ if (commandName == "emptycapped") {
+ // The emptycapped command is a test only command, so we do not expect users to run it with
+ // different WiredTiger configurations on mongod.
+ throw new Error(
+ "Cowardly refusing to run test that uses command 'emptycapped' with the config fuzzer enabled. " +
+ tojson(commandObj));
+ }
+
+ const serverResponse = func.apply(conn, makeFuncArgs(commandObj));
+ return serverResponse;
+}
+
+OverrideHelpers.prependOverrideInParallelShell(
+ "jstests/libs/override_methods/config_fuzzer_incompatible_commands.js");
+
+OverrideHelpers.overrideRunCommand(runCommandOverride);
+})();
diff --git a/jstests/libs/override_methods/implicitly_shard_accessed_collections.js b/jstests/libs/override_methods/implicitly_shard_accessed_collections.js
index 8356f0bfb62..c0ec4649505 100644
--- a/jstests/libs/override_methods/implicitly_shard_accessed_collections.js
+++ b/jstests/libs/override_methods/implicitly_shard_accessed_collections.js
@@ -197,6 +197,11 @@ DB.prototype.getCollection = function() {
try {
TestData.doNotOverrideReadPreference = true;
collStats = this.runCommand({collStats: collection.getName()});
+ if (!collStats.ok && collStats.codeName == "CommandNotSupportedOnView") {
+ // In case we catch CommandNotSupportedOnView it means the collection was actually a
+ // view and should be returned without attempting to shard it (which is not allowed)
+ return collection;
+ }
} finally {
TestData.doNotOverrideReadPreference = testDataDoNotOverrideReadPreferenceOriginal;
}
diff --git a/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js b/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js
index 92199015faf..0d4e0158b3e 100644
--- a/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js
+++ b/jstests/libs/override_methods/implicitly_wrap_pipelines_in_facets.js
@@ -67,7 +67,7 @@ Mongo.prototype.runCommand = function(dbName, cmdObj, options) {
}
cmdObj.pipeline = [
- {$facet: {originalPipeline: originalPipeline}},
+ {$facet: {originalPipeline: originalPipeline, extraPipeline: [{$count: "count"}]}},
{$unwind: '$originalPipeline'},
{$replaceRoot: {newRoot: '$originalPipeline'}},
];
diff --git a/jstests/libs/override_methods/network_error_and_txn_override.js b/jstests/libs/override_methods/network_error_and_txn_override.js
index fc1ec89b266..46e6cc27d5e 100644
--- a/jstests/libs/override_methods/network_error_and_txn_override.js
+++ b/jstests/libs/override_methods/network_error_and_txn_override.js
@@ -844,18 +844,6 @@ function shouldRetryWithNetworkErrorOverride(
return kContinue;
}
- // listCollections and listIndexes called through mongos may return OperationFailed if
- // the request to establish a cursor on the targeted shard fails with a network error.
- //
- // TODO SERVER-30949: Remove this check once those two commands retry on retryable
- // errors automatically.
- if ((cmdName === "listCollections" || cmdName === "listIndexes") &&
- res.code === ErrorCodes.OperationFailed && res.hasOwnProperty("errmsg") &&
- res.errmsg.indexOf("failed to read command response from shard") >= 0) {
- logError("Retrying failed mongos cursor command");
- return kContinue;
- }
-
// Some sharding commands return raw responses from all contacted shards and there won't
// be a top level code if shards returned more than one error code, in which case retry
// if any error is retryable.
diff --git a/jstests/libs/parallelTester.js b/jstests/libs/parallelTester.js
index c0a31e54b86..6241c455ff9 100644
--- a/jstests/libs/parallelTester.js
+++ b/jstests/libs/parallelTester.js
@@ -306,6 +306,7 @@ if (typeof _threadInject != "undefined") {
parallelFilesDir + "/profile_sampling.js",
parallelFilesDir + "/profile_update.js",
parallelFilesDir + "/cached_plan_trial_does_not_discard_work.js",
+ parallelFilesDir + "/timeseries/bucket_unpacking_with_sort_plan_cache.js",
// These tests rely on a deterministically refreshable logical session cache. If they
// run in parallel, they could interfere with the cache and cause failures.
diff --git a/jstests/libs/ttl_util.js b/jstests/libs/ttl_util.js
new file mode 100644
index 00000000000..59c67be7486
--- /dev/null
+++ b/jstests/libs/ttl_util.js
@@ -0,0 +1,40 @@
+/**
+ * Utilities for testing TTL collections.
+ */
+
+load("jstests/libs/fixture_helpers.js");
+
+const TTLUtil = class {
+ /**
+ * Wait until documents inserted before a call to this function have been visited by a TTL
+ * monitor pass. On replica sets, by default the function waits for the TTL deletes to become
+ * visible with read concern 'majority'.
+ *
+ * @param {DB} db Database connection.
+ * @param {boolean} waitForMajorityCommit Only applies when 'db' is from a replica set, set to
+ * false to disable waiting for TTL deletes to become majority commited.
+ */
+ static waitForPass(db, waitForMajorityCommit = true) {
+ // The 'ttl.passes' metric is incremented when the TTL monitor has finished a pass.
+ // Depending on the timing of the pass, seeing an increment of this metric might not
+ // necessarily imply the data we are expecting to be deleted has been seen, as the TTL pass
+ // might have been in progress while the data was inserted. Waiting to see two increases of
+ // this metric guarantees that the TTL has started a new pass after test data insertion.
+ const ttlPasses = db.serverStatus().metrics.ttl.passes;
+ assert.soon(function() {
+ return db.serverStatus().metrics.ttl.passes > ttlPasses + 1;
+ });
+
+ // Readers using a "majority" read concern might expect TTL deletes to be visible after
+ // waitForPass. TTL writes do not imply 'majority' nor 'j: true', and are made durable by
+ // the journal flusher when a flush cycle happens every 'commitIntervalMs'. Even in single
+ // node replica sets, depending on journal flush timing, it is possible that TTL deletes
+ // have not been made durable after returning from this function, and are not considered
+ // majority commited. We force the majority commit point to include the TTL writes up to
+ // this point in time.
+ if (FixtureHelpers.isReplSet(db) && waitForMajorityCommit) {
+ // waitForMajorityCommit will never be true if 'db' is not part of a replica set.
+ FixtureHelpers.awaitLastOpCommitted(db);
+ }
+ }
+};