diff --git a/packages/modules/mongodb/src/mongodb-container.test.ts b/packages/modules/mongodb/src/mongodb-container.test.ts index 1c3a57743..83c82c0b3 100644 --- a/packages/modules/mongodb/src/mongodb-container.test.ts +++ b/packages/modules/mongodb/src/mongodb-container.test.ts @@ -61,4 +61,56 @@ describe("MongodbContainer", { timeout: 240_000 }, () => { await mongodbContainer.stop(); }); // } + + it("uses custom credentials (Mongo4, old commands)", async () => { + const mongodbContainer = await new MongoDBContainer("mongo:4.0.1") + .withUsername("CustomUserName") + .withPassword("CustomPassword") + .start(); + + const db = mongoose.createConnection(mongodbContainer.getConnectionString(), { directConnection: true }); + + const fooCollection = db.collection("foo"); + const obj = { value: 1 }; + + const session = await db.startSession(); + await session.withTransaction(async () => { + await fooCollection.insertOne(obj); + }); + + expect( + await fooCollection.findOne({ + value: 1, + }) + ).toEqual(obj); + + await mongoose.disconnect(); + await mongodbContainer.stop(); + }); + + it("uses custom credentials (Mongo8, new commands)", async () => { + const mongodbContainer = await new MongoDBContainer("mongo:8.0.8") + .withUsername("CustomUserName") + .withPassword("CustomPassword") + .start(); + + const db = mongoose.createConnection(mongodbContainer.getConnectionString(), { directConnection: true }); + + const fooCollection = db.collection("foo"); + const obj = { value: 1 }; + + const session = await db.startSession(); + await session.withTransaction(async () => { + await fooCollection.insertOne(obj); + }); + + expect( + await fooCollection.findOne({ + value: 1, + }) + ).toEqual(obj); + + await mongoose.disconnect(); + await mongodbContainer.stop(); + }); }); diff --git a/packages/modules/mongodb/src/mongodb-container.ts b/packages/modules/mongodb/src/mongodb-container.ts index 0e20be1fc..3453c5cbc 100644 --- a/packages/modules/mongodb/src/mongodb-container.ts +++ b/packages/modules/mongodb/src/mongodb-container.ts @@ -3,6 +3,9 @@ import { AbstractStartedContainer, ExecResult, GenericContainer, StartedTestCont const MONGODB_PORT = 27017; export class MongoDBContainer extends GenericContainer { + private username: string | null = null; + private password: string | null = null; + constructor(image = "mongo:4.0.1") { super(image); this.withExposedPorts(MONGODB_PORT) @@ -11,8 +14,33 @@ export class MongoDBContainer extends GenericContainer { .withStartupTimeout(120_000); } + public withUsername(username: string): this { + this.username = username; + return this; + } + + public withPassword(rootPassword: string): this { + this.password = rootPassword; + return this; + } + public override async start(): Promise { - return new StartedMongoDBContainer(await super.start()); + if (this.username && this.password) { + const containerKeyfilePath = "/tmp/mongo-keyfile"; + this.withCommand([ + "/bin/sh", + "-c", + ` + openssl rand -base64 756 > ${containerKeyfilePath} && + chmod 600 ${containerKeyfilePath} && + chown mongodb:mongodb ${containerKeyfilePath} && + exec mongod --replSet rs0 --keyFile ${containerKeyfilePath} --bind_ip_all + `, + ]); + this.withEnvironment({ MONGO_INITDB_ROOT_USERNAME: this.username, MONGO_INITDB_ROOT_PASSWORD: this.password }); + } + + return new StartedMongoDBContainer(await super.start(), this.username, this.password); } protected override async containerStarted(startedTestContainer: StartedTestContainer): Promise { @@ -20,7 +48,7 @@ export class MongoDBContainer extends GenericContainer { } private async initReplicaSet(startedTestContainer: StartedTestContainer) { - await this.executeMongoEvalCommand(startedTestContainer, "rs.initiate();"); + await this.executeMongoEvalCommand(startedTestContainer, `rs.initiate();`); await this.executeMongoEvalCommand(startedTestContainer, this.buildMongoWaitCommand()); } @@ -30,7 +58,15 @@ export class MongoDBContainer extends GenericContainer { } private buildMongoEvalCommand(command: string) { - return [this.getMongoCmdBasedOnImageTag(), "--eval", command]; + const cmd = [this.getMongoCmdBasedOnImageTag()]; + + if (this.username && this.password) { + cmd.push("--username", this.username, "--password", this.password, "--authenticationDatabase", "admin"); + } + + cmd.push("--eval", command); + + return cmd; } private getMongoCmdBasedOnImageTag() { @@ -58,11 +94,19 @@ export class MongoDBContainer extends GenericContainer { } export class StartedMongoDBContainer extends AbstractStartedContainer { - constructor(startedTestContainer: StartedTestContainer) { + constructor( + startedTestContainer: StartedTestContainer, + private readonly username: string | null, + private readonly password: string | null + ) { super(startedTestContainer); } public getConnectionString(): string { + if (this.username !== null && this.password !== null) { + return `mongodb://${this.username}:${this.password}@${this.getHost()}:${this.getMappedPort(MONGODB_PORT)}`; + } + return `mongodb://${this.getHost()}:${this.getMappedPort(MONGODB_PORT)}`; } }