Skip to content

Commit

Permalink
SERVER-94437 Introduce test runner prelude for interactive mode (#26746)
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 609f32db31f30deae66819c8ecba577dfc191a39
  • Loading branch information
mbroadst authored and MongoDB Bot committed Sep 5, 2024
1 parent 8b94079 commit e948583
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 61 deletions.
4 changes: 0 additions & 4 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ rules:

globals:
TestData: true
# TODO(mbroadst): Remove the next line when ReplSetTest and ShardingTest are converted to modules.
Thread: true
ReplSetTest: true
ShardingTest: true
WriteError: true
WriteCommandError: true
BulkWriteError: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
// Overrides the JS test exiting 'RepliSetTest' fixture to enable reading the change stream in the
// multitenancy and serverless environment.

// Make a copy of the original 'ReplSetTest' fixture.
const originalReplSet = ReplSetTest;

ReplSetTest = function(opts) {
// Setup the 'serverless' environment if the 'opts' is not a connection string, ie. the
// replica-set does not already exist and the replica-set is not part of the sharded cluster,
// ie. 'setParametersMongos' property does not exist.
const newOpts = typeof opts !== "string" && !TestData.hasOwnProperty("setParametersMongos")
? Object.assign({name: "OverridenServerlessChangeStreamReplSet", serverless: true}, opts)
: opts;

// Call the constructor with the original 'ReplSetTest' to populate 'this' with required fields.
originalReplSet.apply(this, [newOpts]);

// Make a copy of the original 'startSetAsync' function and then override it to include the
// required parameters.
this._originalStartSetAsync = this.startSetAsync;
this.startSetAsync = function(options, restart) {
import {
kOverrideConstructor as kOverrideConstructorForRST,
ReplSetTest
} from "jstests/libs/replsettest.js";
import {
kOverrideConstructor as kOverrideConstructorForST,
ShardingTest
} from "jstests/libs/shardingtest.js";

ReplSetTest[kOverrideConstructorForRST] =
class MultitenantChangeStreamReplSetTest extends ReplSetTest {
constructor(opts) {
// Setup the 'serverless' environment if the 'opts' is not a connection string, ie. the
// replica-set does not already exist and the replica-set is not part of the sharded
// cluster, ie. 'setParametersMongos' property does not exist.
const newOpts = typeof opts !== "string" && !TestData.hasOwnProperty("setParametersMongos")
? Object.assign({name: "OverridenServerlessChangeStreamReplSet", serverless: true},
opts)
: opts;

// Call the constructor with the original 'ReplSetTest' to populate 'this' with required
// fields.
super(newOpts);
}

startSetAsync(options, restart, isMixedVersionCluster) {
const newOptions = Object.assign({}, options || {});

let setParameter = {};
Expand All @@ -34,14 +42,11 @@ ReplSetTest = function(opts) {
}

newOptions.setParameter = Object.assign({}, newOptions.setParameter, setParameter);
return this._originalStartSetAsync(newOptions, restart);
};
return super.startSetAsync(newOptions, restart);
}

// Make a copy of the original 'initiate' function and then override it to issue
// 'setChangeStreamState' command.
this._originalInitiate = this.initiate;
this.initiate = function(cfg, initCmd) {
this._originalInitiate(cfg, initCmd, {doNotWaitForPrimaryOnlyServices: false});
initiate(cfg, initCmd) {
super.initiate(cfg, initCmd, {doNotWaitForPrimaryOnlyServices: false});

// Enable the change stream and verify that it is enabled.
const adminDb = this.getPrimary().getDB("admin");
Expand All @@ -57,38 +62,29 @@ ReplSetTest = function(opts) {
}));
assert.eq(explain.stages[0].$cursor.queryPlanner.namespace,
'config.system.change_collection');
};
}
};

// Extend the new 'ReplSetTest' fixture with the properties of the original one.
Object.extend(ReplSetTest, originalReplSet);

// Make a copy of the original 'ShardingTest' fixture.
const originalShardingTest = ShardingTest;

ShardingTest = function(params) {
// Call the original 'ShardingTest' fixture.
const retShardingTest = originalShardingTest.apply(this, [params]);

// For each shard, enable the change stream.
this._rs.forEach((shardSvr) => {
const adminDb = shardSvr.test.getPrimary().getDB("admin");
assert.commandWorked(adminDb.runCommand({setChangeStreamState: 1, enabled: true}));
assert.eq(assert.commandWorked(adminDb.runCommand({getChangeStreamState: 1})).enabled,
true);

// Verify that the change stream cursor is getting opened in the change collection.
const explain = assert.commandWorked(adminDb.getSiblingDB("test").runCommand({
aggregate: 1,
pipeline: [{$changeStream: {}}],
explain: true,
}));
assert.eq(explain.stages[0].$cursor.queryPlanner.namespace,
'config.system.change_collection');
});

return retShardingTest;
ShardingTest[kOverrideConstructorForST] =
class MultitenantChangeStreamShardingTest extends ShardingTest {
constructor(params) {
super(params);

// For each shard, enable the change stream.
this._rs.forEach((shardSvr) => {
const adminDb = shardSvr.test.getPrimary().getDB("admin");
assert.commandWorked(adminDb.runCommand({setChangeStreamState: 1, enabled: true}));
assert.eq(assert.commandWorked(adminDb.runCommand({getChangeStreamState: 1})).enabled,
true);

// Verify that the change stream cursor is getting opened in the change collection.
const explain = assert.commandWorked(adminDb.getSiblingDB("test").runCommand({
aggregate: 1,
pipeline: [{$changeStream: {}}],
explain: true,
}));
assert.eq(explain.stages[0].$cursor.queryPlanner.namespace,
'config.system.change_collection');
});
}
};

// Extend the new 'ShardingTest' fixture with the properties of the original one.
Object.extend(ShardingTest, originalShardingTest);
5 changes: 5 additions & 0 deletions src/mongo/scripting/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ extern const JSFile db;
extern const JSFile explain_query;
extern const JSFile explainable;
extern const JSFile mongo;
extern const JSFile prelude;
extern const JSFile session;
extern const JSFile query;
extern const JSFile utils;
Expand All @@ -386,6 +387,10 @@ void Scope::execCoreFiles() {
execSetup(JSFiles::explainable);
}

void Scope::execPrelude() {
execSetup(JSFiles::prelude);
}

namespace {

class ScopeCache {
Expand Down
1 change: 1 addition & 0 deletions src/mongo/scripting/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class Scope {
int timeoutMs = 0);

void execCoreFiles();
void execPrelude();

virtual void loadStored(OperationContext* opCtx, bool ignoreNotConnected = false);

Expand Down
1 change: 1 addition & 0 deletions src/mongo/shell/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ MONGOJS_CPP_JSFILES = [
"explain_query.js",
"explainable.js",
"mongo.js",
"prelude.js",
"query.js",
"session.js",
"types.js",
Expand Down
3 changes: 3 additions & 0 deletions src/mongo/shell/mongo_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,9 @@ int mongo_main(int argc, char* argv[]) {

shellHistoryInit();

// Only run the prelude in interactive mode
scope->execPrelude();

std::string prompt;
int promptType;

Expand Down
10 changes: 10 additions & 0 deletions src/mongo/shell/prelude.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This prelude is run before any interactive test runner session.
try {
const {ReplSetTest} = await import("jstests/libs/replsettest.js");
globalThis.ReplSetTest = ReplSetTest;

const {ShardingTest} = await import("jstests/libs/shardingtest.js");
globalThis.ShardingTest = ShardingTest;
} catch (e) {
// ignore all errors
}
2 changes: 2 additions & 0 deletions src/mongo/shell/utils_sh.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global ShardingTest */

var sh = function() {
return "try sh.help();";
};
Expand Down

0 comments on commit e948583

Please sign in to comment.