Skip to content

Conversation

@prestonvasquez
Copy link
Contributor

@prestonvasquez prestonvasquez commented Aug 8, 2025

What does this PR do?

This PR introducing a new module for spinning up a MongoDB Atlas Local instance in Docker for integration testing. See documentation for mongodb/mongodb-atlas-local.

Why is it important?

This module enables users to test against Atlas Search and Atlas Vector Search in CI environments without needing to start a live MongoDB Atlas cluster. While users sometimes attempt to use mongodb/mongodb-atlas-local with the standard mongodb package—which may appear to work because of the port 27017 wait condition—the underlying options are actually incompatible. For more details, see the related issue.

Related issues

@netlify
Copy link

netlify bot commented Aug 8, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit aab5560
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/68cbcbc177a1e50008dd6fce
😎 Deploy Preview https://deploy-preview-3254--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@prestonvasquez prestonvasquez force-pushed the feat/atlaslocal-module branch from 242d6d9 to 2e72f6f Compare August 8, 2025 21:01
@prestonvasquez prestonvasquez changed the title feat(modules/atlaslocal): add MongoDB Atlas Local module feat(atlaslocal): add MongoDB Atlas Local module Aug 8, 2025
@prestonvasquez prestonvasquez changed the title feat(atlaslocal): add MongoDB Atlas Local module feat: add MongoDB Atlas Local module Aug 8, 2025
@prestonvasquez prestonvasquez changed the title feat: add MongoDB Atlas Local module feat(atlaslocal): add MongoDB Atlas Local module Aug 8, 2025
@prestonvasquez prestonvasquez marked this pull request as ready for review August 8, 2025 21:16
@prestonvasquez prestonvasquez requested a review from a team as a code owner August 8, 2025 21:16
@prestonvasquez prestonvasquez changed the title feat(atlaslocal): add MongoDB Atlas Local module feat(atlaslocal): add MongoDB Atlas Local module Aug 8, 2025
Copy link
Member

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @prestonvasquez thanks for such a well-structured PR, the code is following our patterns in the majority of the places, and is pretty easy to read, thanks for this.

I left a couple of comments, but let me start with this: do we need a separate module for Atlas, or is it fine to have an specialised container in the MongoDB module? I ask because in tc-java the atlas container lives in the same JAR file. Of course, we are not coupled to that, but as you work for Mongo, you probably have a better opinion on this. Let's discuss this and then we can move forward with the rest of the review 🙏

Cheers!

// WithMongotLogFile sets the path to the file where you want to store the logs
// of Atlas Search (mongot) by setting the MONGOT_LOG_FILE environment variable.
// Note that this can be set to /dev/stdout or /dev/stderr for convenience.
func WithMongotLogFile(logFile string) testcontainers.CustomizeRequestOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: should we simplify the API adding types for stdout/stderr so users don't configure a non existing path? same for the runner log file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Suggest we use the following functions:

WithMongotLogToStdout()
WithMongotLogToStderr()
WithMongotLogToFile() // always logs to "/tmp/mongot.log"

And then a convenience to copy (wrapping CopyFileFromContainer) the file from container:

(*Container) CopyMongotLog(ctx context.Context) (io.ReadCloser, error) {} 

@mdelapenya mdelapenya self-assigned this Aug 25, 2025
@mdelapenya mdelapenya added the feature New functionality or new behaviors on the existing one label Aug 25, 2025
@prestonvasquez
Copy link
Contributor Author

prestonvasquez commented Aug 25, 2025

@mdelapenya Thanks for the thoughtful feedback!

do we need a separate module for Atlas, or is it fine to have an specialised container in the MongoDB module?

I have the following concerns with combining atlaslocal with the mongodb module:

  1. This would introduce namespace conflicts, i.e. mongodb.WithUsername does something different than atlaslocal.WithUsername and for safety I would recommend not combining the behavior:
func WithUsername(username string) testcontainers.CustomizeRequestOption {
	return func(req *testcontainers.GenericContainerRequest) error {
		if username != "" {
			req.Env["MONGODB_INITDB_ROOT_USERNAME"] = username // atlaslocal
			req.Env["MONGO_INITDB_ROOT_USERNAME"] = username   // mongodb
		}

		return nil
	}
}
  1. New mongodb.WithAtlasLocal* functional options are somewhat awkward and could be confusing: “Why doesn’t mongodb.WithAtlasLocalUsername work with mongodb.Run()?”
  2. Having mongodb.RunAtlasLocal() pull a different image than mongodb.Run() is surprising and breaks user expectations.
  3. Atlas Local will likely be popular, and burying it inside the existing MongoDB documentation may risk hiding it from users.

I agree that atlaslocal could be more obviously represented as a mongodb product. Would adding a module to mongodb be an acceptable solution? This seems to have precedent.

package main 

import (
​​	"github.com/testcontainers/testcontainers-go/modules/mongodb/atlaslocal"
)

// ...

If this is acceptable, could we retain the existing documentation for this at docs/modules/atlaslocal? If this isn’t straightforward (e.g. embedded code examples), then I’d be happy to update the existing mongodb documentation to include atlaslocal.

@mdelapenya
Copy link
Member

@prestonvasquez thanks for your detailed response. Indeed, I think following the gcloud module pattern would be of interest, having a mongodb/atlas or mongodb/atlaslocal submodule would work. I'd start without reusing any from the mongodb module, and let the usage discover shared components.

About the docs, I'm totally fine if the entry is MongoDB Atlas Local. Just name it in the mkdocs.yml with that title and in the proper order, next to the existing MongoDB module. You can test the docs with make serve-docs.

This PR looks promising!

@prestonvasquez prestonvasquez force-pushed the feat/atlaslocal-module branch from 3e4dfef to ba459ad Compare August 28, 2025 21:37
github.com/testcontainers/testcontainers-go v0.38.0
go.mongodb.org/mongo-driver v1.13.1
go.mongodb.org/mongo-driver v1.17.4
go.mongodb.org/mongo-driver/v2 v2.3.0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdelapenya Combining atlaslocal into modules/mongodb requires that we maintain both v1 and v2 in the go.mod file. I anticipate that the Go toolchain will prune this data from the dependency topology if a user is only using modules/mongodb. Perhaps we should open a ticket to create a v2 for the mongodb testcontainer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevenh wdyt? I really don't like how Go manages major versions, as it comes with a lot of maintenance work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@prestonvasquez sorry for the delay, I'm in a conference. Let's make progress by keeping the original approach of a separate atlaslocal module. The fact that the project name is relevant enough and we bypass the dependencies issue you raised, makes me think it deserves to have its own namespace. Sorry for the back and forth 🙏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I'm another member of the MongoDB Go Driver team. It's easy to update the MongoDB Go Driver to v2 in the mongodb module (see #3278). I'm in favor of keeping atlaslocal in the mongodb module.

@mdelapenya are there other reasons to separate the atlaslocal module, or just the v1/v2 driver dependency?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @matthewdale thanks for the bump PR. Given the driver version matches, we can keep this one as a submodule of the parent mongodb module. I just wanted to make progress and not block you with this one.


// Run creates an instance of the MongoDBAtlasLocal container type
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error) {
req := testcontainers.ContainerRequest{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: we are moving away from defining the container request here in the modules, but instead using the more modern testcontainers.Run function, which needs an slice of options. You basically need to convert the current req struct into the options slice.

PS: I can do this for you if you feel it's too much work for the review. I would do it adding commits on top of yours, or in a follow-up

github.com/testcontainers/testcontainers-go v0.38.0
go.mongodb.org/mongo-driver v1.13.1
go.mongodb.org/mongo-driver v1.17.4
go.mongodb.org/mongo-driver/v2 v2.3.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevenh wdyt? I really don't like how Go manages major versions, as it comes with a lot of maintenance work

@prestonvasquez prestonvasquez force-pushed the feat/atlaslocal-module branch 2 times, most recently from 8167d6c to d284caa Compare August 29, 2025 18:09
@matthewdale
Copy link
Contributor

@mdelapenya Preston is going to be out for a few weeks, is it possible to move this PR along while he's out?

I see that merging #3278 created merge conflicts in this PR. I've created a merge commit matthewdale@ce8a9e4 that resolves the conflicts. I made that commit by running:

gh pr checkout 3254
git merge main
git checkout main modules/mongodb/go.* modules/mongodb/*.go
git merge --continue

Are you able to resolve the merge conflicts? Are there any other changes needed before merging this PR?

@mdelapenya
Copy link
Member

@matthewdale sure, I can take over and resolve the conflicts by myself, and push the changes to this branch.

Thanks for letting me know 🙏

* main: (22 commits)
  chore(deps): bump golang.org/x/net from 0.28.0 to 0.38.0 (testcontainers#3299)
  feat: allow saving specific platforms for an image (testcontainers#3218)
  chore(deps): bump dario.cat/mergo from 1.0.1 to 1.0.2 (testcontainers#3238)
  chore(deps): bump golang.org/x/sys from 0.32.0 to 0.36.0 (testcontainers#3282)
  feat(redpanda): add support for http proxy (testcontainers#3258)
  chore(deps): bump github/codeql-action from 3.29.3 to 3.30.3 (testcontainers#3287)
  chore(go): bump to Go 1.24 as minimal version (testcontainers#3298)
  deps(mongodb): update MongoDB Go Driver to v2 (testcontainers#3278)
  chore(deps): bump github.com/shirou/gopsutil/v4 from 4.25.5 to 4.25.6 (testcontainers#3224)
  chore(deps): bump mkdocs-include-markdown-plugin from 7.1.6 to 7.1.7 (testcontainers#3284)
  docs: clarify no client SDKs in production modules/images, in contributing.md (testcontainers#3279)
  chore(deps): bump github.com/docker/go-connections from 0.5.0 to 0.6.0 (testcontainers#3285)
  chore(deps): bump tj-actions/changed-files from 46.0.3 to 47.0.0 (testcontainers#3283)
  chore(modulegen): detect missing project files after new module creation (testcontainers#3281)
  chore(deps): bump github.com/docker/docker in /modules/nebulagraph (testcontainers#3277)
  feat(nebulagraph): add NebulaGraph module (testcontainers#3266)
  fix: preserve unix socket schema in testcontainers host from properties (testcontainers#3213)
  feat(registry): add helper functions to pull and tag images (testcontainers#3275)
  fix(reaper): remove termSignal override (testcontainers#3261)
  chore(deps): bump ryuk to v0.13.0, which uses scratch as base image (testcontainers#3274)
  ...
It also resolves some Ipv6 issues
@coderabbitai
Copy link

coderabbitai bot commented Sep 18, 2025

Summary by CodeRabbit

  • New Features
    • Added a MongoDB Atlas Local module for Testcontainers-Go: run a local Atlas-like MongoDB with SCRAM auth (inline/file), init database/scripts, and configurable mongot/runner logging. Provides a connection string helper and log readers. Examples included.
  • Documentation
    • New module page and navigation entry.
  • Tests
    • Comprehensive integration and example tests for connectivity, auth, init scripts, logging, and connection strings.
  • Refactor
    • Standardized MongoDB default port handling with no behavior changes.

Walkthrough

Adds a new MongoDB Atlas Local module: implementation (container wrapper and options), docs and mkdocs nav entry, integration tests and examples, plus connection-string and log-reading APIs for Testcontainers-Go.

Changes

Cohort / File(s) Summary
Docs
docs/modules/mongodb-atlaslocal.md
New documentation page describing the mongodb-atlas-local module: purpose, usage, Run signature, options, container methods, and examples.
Site config
mkdocs.yml
Inserts modules/mongodb-atlaslocal.md into the Modules navigation.
Module code
modules/mongodb/atlaslocal/atlaslocal.go, modules/mongodb/atlaslocal/options.go
New module implementation: Container type embedding testcontainers.Container, Run constructor, Option API (credentials inline/file, telemetry, init DB/scripts, log routing), env/file-mount handling, connection-string builder, and log readers.
Tests & examples
modules/mongodb/atlaslocal/atlaslocal_test.go, modules/mongodb/atlaslocal/examples_test.go
New integration tests covering connectivity, SCRAM auth modes, telemetry, init scripts, logging, and connection-string parsing; example usage tests showing run/connect/read-logs.
Refactor (port const)
modules/mongodb/mongodb.go
Extracts defaultPort = "27017/tcp" and replaces literal port occurrences with the constant.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev
  participant Run as atlaslocal.Run
  participant Docker as Docker Engine
  participant C as Container
  participant Mongo as MongoDB (in Container)

  Dev->>Run: Run(ctx, image, opts...)
  Run->>Run: apply & validate options (env, mounts, logs)
  Run->>Docker: create & start container (expose 27017/tcp, wait)
  Docker-->>Run: container ready
  Run-->>Dev: *Container

  Dev->>C: ConnectionString(ctx)
  C-->>Dev: mongodb://host:port[/db]/?directConnection=true[&authSource=admin]

  Dev->>Mongo: connect & ping
  Mongo-->>Dev: OK

  alt read mongot logs
    Dev->>C: ReadMongotLogs(ctx)
    C-->>Dev: io.ReadCloser (file or stdout/stderr)
  end

  alt read runner logs
    Dev->>C: ReadRunnerLogs(ctx)
    C-->>Dev: io.ReadCloser (file or stdout/stderr)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A hop, a skip, containers spin,
Atlas wakes with mongot grin.
Secrets snug in burrowed files,
Init scripts march in tidy piles.
Logs to stdout, stderr too—
Test burrows bloom with something new. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The only change that appears outside the linked-issue scope is a small refactor in modules/mongodb/mongodb.go introducing a defaultPort constant and replacing literal "27017/tcp" references; this touches an existing module rather than the new atlaslocal module and was not requested by issue #3244. Either revert the modules/mongodb/mongodb.go refactor from this branch and submit it as a separate, self-contained refactor PR, or add a brief justification in this PR description and ensure tests and consumers are unaffected before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 51.35% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat(atlaslocal): add MongoDB Atlas Local module" is concise, follows conventional commit style, and accurately summarizes the primary change (adding a dedicated atlaslocal module) so a reviewer scanning history will understand the main purpose of the PR.
Linked Issues Check ✅ Passed The changes implement a new modules/mongodb/atlaslocal package (Run, options, log helpers), add docs and a mkdocs navigation entry, and include comprehensive tests and examples exercising Atlas Search features, which collectively satisfy the linked issue objectives to provide first-class support for mongodb-atlas-local, enable Atlas-specific integration testing, and offer a discoverable, dedicated module.
Description Check ✅ Passed The PR description explains the addition of a new module for mongodb/mongodb-atlas-local, the motivation (enable Atlas-specific testing without a remote cluster), and links the related issue (#3244), so it is directly related to the changeset and provides helpful reviewer context.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (19)
mkdocs.yml (1)

103-103: Consider renaming the documentation entry to "MongoDB Atlas Local"

Per the reviewer's comment, the documentation entry should be titled "MongoDB Atlas Local" for clarity and consistency with the module's full name.

Apply this change to improve documentation clarity:

-        - modules/mongodb-atlaslocal.md
+        - MongoDB Atlas Local: modules/mongodb-atlaslocal.md
modules/mongodb/atlaslocal/options.go (4)

32-33: Typo in struct field name

The field localUsernamefile should be localUsernameFile for consistency with Go naming conventions.

Apply this fix:

-	localUsernamefile string
+	localUsernameFile string

89-89: Update field references after fixing typo

After fixing the typo in the struct field name, update all references throughout the file.

Apply these fixes to update references:

-	usernameFile := opts.localUsernamefile
+	usernameFile := opts.localUsernameFile
-	if opts.username == "" && opts.localUsernamefile == "" {
+	if opts.username == "" && opts.localUsernameFile == "" {
-	if opts.username != "" && opts.localUsernamefile != "" {
+	if opts.username != "" && opts.localUsernameFile != "" {
-	r, err := os.ReadFile(opts.localUsernamefile)
+	r, err := os.ReadFile(opts.localUsernameFile)
-		opts.localUsernamefile = usernameFile
+		opts.localUsernameFile = usernameFile

Also applies to: 111-111, 115-115, 123-123, 205-205


148-148: Incorrect module reference in comment

The comment incorrectly references "Redpanda container" instead of "MongoDB Atlas Local container".

Apply this fix:

-// Option is an option for the Redpanda container.
+// Option is an option for the MongoDB Atlas Local container.

298-337: Decide whether WithInitScripts should be additive or replace previous mounts

Verification: most modules implement additive semantics (append init scripts) — e.g. modules/postgres/postgres.go, modules/clickhouse/clickhouse.go, modules/cockroachdb/options.go, modules/cassandra/cassandra.go. modules/mongodb/atlaslocal currently replaces prior mounts (documented in modules/mongodb/atlaslocal/options.go and asserted by modules/mongodb/atlaslocal/atlaslocal_test.go TestWithInitScripts_MultipleScripts).

Action: choose one:

  • Make atlaslocal additive to match other modules (remove the filter that clears /docker-entrypoint-initdb.d), or
  • Keep replacement behavior but make it explicit in the public API/docs (or add an explicitly additive variant) and update docs/tests accordingly.
docs/modules/mongodb-atlaslocal.md (2)

18-20: Add language specifier to fenced code block

The fenced code block should specify the language for proper syntax highlighting.

Apply this fix:

-```
+```bash
go get github.com/testcontainers/testcontainers-go/modules/mongodb/atlaslocal

74-74: Fix typo: missing space after "at"

There's a missing space between "at" and the backtick in the documentation.

Apply this fix:

-This functional option mounts a local file as the MongoDB root username secret at`/run/secrets/mongo-root-username`
+This functional option mounts a local file as the MongoDB root username secret at `/run/secrets/mongo-root-username`
modules/mongodb/atlaslocal/atlaslocal.go (4)

63-66: Use the declared constant instead of a magic string.

Minor consistency/readability nit: reuse defaultPort.

-	endpoint, err := ctr.PortEndpoint(ctx, "27017/tcp", "")
+	endpoint, err := ctr.PortEndpoint(ctx, defaultPort, "")

109-113: Fix comment grammar.

“This method returns …” (plural agreement).

-// This method return the os.ErrNotExist sentinel error if it is called with
+// This method returns the os.ErrNotExist sentinel error if it is called with

127-132: Fix comment grammar and remove parentheses from method name.

Keep comments consistent with Go doc style.

-// ReadRunnerLogs() returns a reader for runner logs in the container. Reads
+// ReadRunnerLogs returns a reader for runner logs in the container. Reads
@@
-// This method return the os.ErrNotExist sentinel error if it is called with
+// This method returns the os.ErrNotExist sentinel error if it is called with

37-42: Guard healthcheck wait or pin image tag

Confirmed: mongodb/mongodb-atlas-local includes a built-in HEALTHCHECK (MongoDB docs), but Feb 2025 image pushes caused intermittent healthcheck/unhealthy failures — pinning to a known-good tag/sha was recommended.

File: modules/mongodb/atlaslocal/atlaslocal.go (lines 37–42)

  • Make wait.ForHealthCheck() optional or implement a fallback to wait.ForListeningPort(defaultPort) if the healthcheck times out (or expose this as a configurable option).
  • Alternatively, pin the image to a stable tag/sha and document the HEALTHCHECK requirement and rationale.
modules/mongodb/atlaslocal/atlaslocal_test.go (8)

28-31: Schedule cleanup only after ensuring container creation succeeded.

Calling CleanupContainer(t, ctr) before require.NoError(t, err) risks a typed-nil panic if Run fails. Reorder to assert first, then schedule cleanup. Apply this pattern across all tests.

Example diffs:

@@ TestMongoDBAtlasLocal
-	ctr, err := atlaslocal.Run(ctx, latestImage)
-	testcontainers.CleanupContainer(t, ctr)
-	require.NoError(t, err)
+	ctr, err := atlaslocal.Run(ctx, latestImage)
+	require.NoError(t, err)
+	testcontainers.CleanupContainer(t, ctr)
@@ inside TestSCRAMAuth loop
-	ctr, err := atlaslocal.Run(context.Background(), latestImage, opts...)
-	testcontainers.CleanupContainer(t, ctr)
+	ctr, err := atlaslocal.Run(context.Background(), latestImage, opts...)
 	...
-	require.NoError(t, err)
+	require.NoError(t, err)
+	testcontainers.CleanupContainer(t, ctr)
@@ TestWithNoTelemetry subtests (apply to both)
-	ctr, err := atlaslocal.Run(context.Background(), latestImage, atlaslocal.WithNoTelemetry())
-	testcontainers.CleanupContainer(t, ctr)
-	require.NoError(t, err)
+	ctr, err := atlaslocal.Run(context.Background(), latestImage, atlaslocal.WithNoTelemetry())
+	require.NoError(t, err)
+	testcontainers.CleanupContainer(t, ctr)

(Repeat the same ordering fix in all places listed in the line ranges.)

Also applies to: 190-203, 229-232, 237-240, 247-249, 258-261, 269-275, 284-289, 301-308, 311-318, 321-327, 334-341, 360-363, 437-440, 482-485, 533-535


573-582: Close the reader to avoid leaking resources.

ReadMongotLogs returns an io.ReadCloser that must be closed.

 func requireMongotLogs(t *testing.T, ctr testcontainers.Container) {
@@
-	reader, err := ctr.(*atlaslocal.Container).ReadMongotLogs(context.Background())
+	reader, err := ctr.(*atlaslocal.Container).ReadMongotLogs(context.Background())
 	require.NoError(t, err)
-
-	buf := make([]byte, 1)
-	_, _ = reader.Read(buf) // read at least one byte to ensure non-empty
+	defer reader.Close()
+	buf := make([]byte, 1)
+	_, _ = reader.Read(buf) // read at least one byte to ensure non-empty
 }

565-571: Generic assertion message for env var helper.

Message currently always mentions DO_NOT_TRACK, which is misleading for other vars.

-	require.Equal(t, expected, out, "DO_NOT_TRACK env var value mismatch")
+	require.Equalf(t, expected, out, "%s env var value mismatch", envVarName)

724-733: Don’t assert on ls exit code; directory presence varies by image.

/docker-entrypoint-initdb.d may exist even when empty, making the “no scripts” case brittle. Drop the exit-code assertion; rely on per-file checks below.

-	exit, r, err := ctr.Exec(context.Background(), []string{"sh", "-lc", "ls -l " + dstDir})
+	exit, r, err := ctr.Exec(context.Background(), []string{"sh", "-lc", "ls -l " + dstDir + " || true"})
 	require.NoError(t, err)
-
-	// If the map is empty, the command returns exit code 2.
-	if len(expectedScripts) == 0 {
-		require.Equal(t, 2, exit)
-	} else {
-		require.Equal(t, 0, exit)
-	}
+	_ = exit // ignore, we read listing instead

760-768: Make path absolute and avoid relying on cd success.

Use an absolute path and list defensively; then assert absence.

-	for filename := range expectedScripts {
-		cmd := []string{"sh", "-c", "cd docker-entrypoint-initdb.d && ls -l"}
-
-		exitCode, reader, err := ctr.Exec(context.Background(), cmd)
-		require.NoError(t, err)
-		require.Zero(t, exitCode, "Expected exit code 0 for command: %v", cmd)
-
-		content, _ := io.ReadAll(reader)
-		require.NotContains(t, string(content), filename)
-	}
+	for filename := range expectedScripts {
+		cmd := []string{"sh", "-lc", "ls -1 /docker-entrypoint-initdb.d 2>/dev/null || true"}
+		_, reader, err := ctr.Exec(context.Background(), cmd)
+		require.NoError(t, err)
+		content, _ := io.ReadAll(reader)
+		require.NotContains(t, string(content), filename)
+	}

542-549: Avoid brittle host slicing; parse host:port instead.

Providers may return different hosts (e.g., 127.0.0.1). Split and assert both parts are non-empty.

-	require.Equal(t, "mongodb", connString.Scheme)
-	require.Equal(t, "localhost", connString.Hosts[0][:9])
-	require.NotEmpty(t, connString.Hosts[0][10:], "Port should be non-empty")
+	require.Equal(t, "mongodb", connString.Scheme)
+	host, port, err := net.SplitHostPort(connString.Hosts[0])
+	require.NoError(t, err, "Invalid host:port")
+	require.NotEmpty(t, host)
+	require.NotEmpty(t, port)

Add:

+	"net"

to the imports at the top of the test file.


793-804: Use restrictive permissions for secret test files.

Prefer 0600 for username/password test files to mirror secret semantics and avoid permissive perms.

-	err := os.WriteFile(usernameFilepath, []byte("file_testuser"), 0o755)
+	err := os.WriteFile(usernameFilepath, []byte("file_testuser"), 0o600)
@@
-	err = os.WriteFile(passwordFilepath, []byte("file_testpass"), 0o755)
+	err = os.WriteFile(passwordFilepath, []byte("file_testpass"), 0o600)

23-24: Pin test image to a known tag (or digest) for stability.

Using :latest can introduce flakiness when the upstream image changes. Consider pinning to a specific tag or digest, optionally allowing override via env.

-const latestImage = "mongodb/mongodb-atlas-local:latest"
+const latestImage = "mongodb/mongodb-atlas-local:7.0.14" // example; consider updating to a tested tag or allow override via env

Would you like me to check the currently recommended stable tag and open a follow-up PR to pin and document it?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fdd847 and 4c9cfe7.

📒 Files selected for processing (7)
  • .github/dependabot.yml (1 hunks)
  • docs/modules/mongodb-atlaslocal.md (1 hunks)
  • mkdocs.yml (1 hunks)
  • modules/mongodb/atlaslocal/atlaslocal.go (1 hunks)
  • modules/mongodb/atlaslocal/atlaslocal_test.go (1 hunks)
  • modules/mongodb/atlaslocal/examples_test.go (1 hunks)
  • modules/mongodb/atlaslocal/options.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
modules/mongodb/atlaslocal/examples_test.go (4)
modules/mongodb/examples_test.go (2)
  • ExampleRun (16-42)
  • ExampleRun_connect (44-82)
modules/mongodb/atlaslocal/atlaslocal.go (1)
  • Run (23-57)
cleanup.go (1)
  • TerminateContainer (97-108)
modules/mongodb/atlaslocal/options.go (2)
  • WithMongotLogFile (363-369)
  • WithRunnerLogFile (393-399)
modules/mongodb/atlaslocal/atlaslocal.go (5)
options.go (4)
  • ContainerCustomizer (22-24)
  • WithExposedPorts (464-469)
  • WithWaitStrategy (376-378)
  • WithFiles (534-539)
modules/mongodb/atlaslocal/options.go (1)
  • Option (149-149)
wait/all.go (1)
  • ForAll (44-48)
wait/host_port.go (1)
  • ForListeningPort (67-69)
wait/health.go (1)
  • ForHealthCheck (55-57)
modules/mongodb/atlaslocal/atlaslocal_test.go (4)
modules/mongodb/atlaslocal/atlaslocal.go (2)
  • Run (23-57)
  • Container (17-20)
testing.go (1)
  • CleanupContainer (91-97)
options.go (1)
  • ContainerCustomizer (22-24)
modules/mongodb/atlaslocal/options.go (13)
  • WithUsername (159-167)
  • WithPassword (171-179)
  • WithUsernameFile (184-215)
  • WithPasswordFile (220-251)
  • WithNoTelemetry (255-261)
  • WithMongotLogFile (363-369)
  • WithMongotLogToStdout (343-349)
  • WithMongotLogToStderr (353-359)
  • WithRunnerLogFile (393-399)
  • WithRunnerLogToStdout (373-379)
  • WithRunnerLogToStderr (383-389)
  • WithInitDatabase (266-272)
  • WithInitScripts (278-339)
modules/mongodb/atlaslocal/options.go (3)
options.go (1)
  • ContainerCustomizer (22-24)
container.go (1)
  • ContainerFile (110-115)
generic.go (1)
  • GenericContainerRequest (21-27)
🪛 markdownlint-cli2 (0.17.2)
docs/modules/mongodb-atlaslocal.md

18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (1)
modules/mongodb/atlaslocal/examples_test.go (1)

1-174: LGTM! Well-structured examples

The example tests are comprehensive and follow best practices:

  • Proper cleanup with defer and TerminateContainer
  • Clear demonstration of each feature (basic run, connection, log reading)
  • Appropriate error handling throughout
  • Good use of example output comments for testable examples

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/mongodb/mongodb.go (1)

57-66: Bug: reading env from the wrong request object breaks auth/replica set logic.

Options mutate genericContainerReq.Env, but the code reads req.Env (the original, unmodified struct). This causes missing validation and replica set misconfiguration when options are provided.

Apply this diff:

-	username := req.Env["MONGO_INITDB_ROOT_USERNAME"]
-	password := req.Env["MONGO_INITDB_ROOT_PASSWORD"]
+	username := genericContainerReq.Env["MONGO_INITDB_ROOT_USERNAME"]
+	password := genericContainerReq.Env["MONGO_INITDB_ROOT_PASSWORD"]
@@
-	replicaSet := req.Env[replicaSetOptEnvKey]
+	replicaSet := genericContainerReq.Env[replicaSetOptEnvKey]

Also applies to: 68-73

🧹 Nitpick comments (10)
modules/mongodb/mongodb.go (1)

196-199: Avoid hard-coding internal port "27017" in replica set initiation.

Define a single internal port constant to prevent future drift.

Apply this diff:

@@
-const (
-	defaultPort         = "27017/tcp"
+const (
+	defaultPort         = "27017/tcp"
+	internalMongoPort   = "27017"
@@
-					cmd := cli.eval(
-						"rs.initiate({ _id: '%s', members: [ { _id: 0, host: '%s:27017' } ] })",
-						replSetName,
-						ip,
-					)
+					cmd := cli.eval(
+						"rs.initiate({ _id: '%s', members: [ { _id: 0, host: '%s:%s' } ] })",
+						replSetName,
+						ip,
+						internalMongoPort,
+					)
docs/modules/mongodb-atlaslocal.md (2)

18-20: Fix markdownlint MD040: add language to fenced block.

Apply this diff:

-```
-go get github.com/testcontainers/testcontainers-go/modules/mongodb/atlaslocal
-```
+```bash
+go get github.com/testcontainers/testcontainers-go/modules/mongodb/atlaslocal
+```

37-39: Use "go" as the code fence language (not "golang").

This matches common tooling/highlighters.

Apply this diff:

-```golang
+```go
 func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error)

</blockquote></details>
<details>
<summary>modules/mongodb/atlaslocal/atlaslocal_test.go (7)</summary><blockquote>

`28-31`: **Schedule container cleanup only after successful start.**

Calling CleanupContainer before verifying err may pass a nil container to the terminator in failure cases.

Apply this pattern across the file:

```diff
-ctr, err := atlaslocal.Run(context.Background(), latestImage, opts...)
-testcontainers.CleanupContainer(t, ctr)
-require.NoError(t, err)
+ctr, err := atlaslocal.Run(context.Background(), latestImage, opts...)
+require.NoError(t, err)
+testcontainers.CleanupContainer(t, ctr)

Also applies to: 191-201, 229-233, 237-241, 246-250, 258-261, 269-276, 284-288, 302-305, 311-315, 321-325, 335-339, 360-363, 438-440, 483-485, 533-536


543-549: Make host/port assertions robust.

String slicing assumes "localhost:". Use net.SplitHostPort and don’t assume the host literal.

Apply this diff:

@@
-			require.Equal(t, "mongodb", connString.Scheme)
-			require.Equal(t, "localhost", connString.Hosts[0][:9])
-			require.NotEmpty(t, connString.Hosts[0][10:], "Port should be non-empty")
+			require.Equal(t, "mongodb", connString.Scheme)
+			host, port, err := net.SplitHostPort(connString.Hosts[0])
+			require.NoError(t, err)
+			require.NotEmpty(t, port, "Port should be non-empty")
+			require.Contains(t, []string{"localhost", "127.0.0.1"}, host)

And add the import:

 import (
 	"context"
 	"io"
-	"math/rand/v2"
+	"math/rand"
+	"net"
 	"os"

23-24: Allow overriding the test image via env var to improve reproducibility.

Pinning or overriding avoids flakiness from the moving "latest" tag.

Apply this diff:

-const latestImage = "mongodb/mongodb-atlas-local:latest"
+var latestImage = func() string {
+	if v := os.Getenv("ATLASLOCAL_IMAGE"); v != "" {
+		return v
+	}
+	return "mongodb/mongodb-atlas-local:latest"
+}()

566-572: Clarify assertion message in requireEnvVar().

Use the actual env var name in failure messages.

Apply this diff:

-	out := strings.TrimSpace(string(outBytes[8:]))
-	require.Equal(t, expected, out, "DO_NOT_TRACK env var value mismatch")
+	out := strings.TrimSpace(string(outBytes[8:]))
+	require.Equal(t, expected, out, "%s env var value mismatch", envVarName)

762-766: Use absolute path when listing init scripts.

Avoid relying on container working directory.

Apply this diff:

-		cmd := []string{"sh", "-c", "cd docker-entrypoint-initdb.d && ls -l"}
+		cmd := []string{"sh", "-c", "ls -l /docker-entrypoint-initdb.d"}

795-806: Use restrictive permissions for credential files in tests.

Secrets should not be executable.

Apply this diff:

-	err := os.WriteFile(usernameFilepath, []byte("file_testuser"), 0o755)
+	err := os.WriteFile(usernameFilepath, []byte("file_testuser"), 0o600)
@@
-	err = os.WriteFile(passwordFilepath, []byte("file_testpass"), 0o755)
+	err = os.WriteFile(passwordFilepath, []byte("file_testpass"), 0o600)

574-584: Consider checking Read*Logs actually returns data.

Read and assert n > 0 to avoid false positives if EOF.

Apply this diff:

-	buf := make([]byte, 1)
-	_, _ = reader.Read(buf) // read at least one byte to ensure non-empty
+	buf := make([]byte, 1)
+	n, err := reader.Read(buf)
+	require.Greater(t, n, 0)

And similarly in requireRunnerLogs.

Also applies to: 601-609

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f145a9 and aab5560.

📒 Files selected for processing (5)
  • docs/modules/mongodb-atlaslocal.md (1 hunks)
  • modules/mongodb/atlaslocal/atlaslocal.go (1 hunks)
  • modules/mongodb/atlaslocal/atlaslocal_test.go (1 hunks)
  • modules/mongodb/atlaslocal/options.go (1 hunks)
  • modules/mongodb/mongodb.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • modules/mongodb/atlaslocal/atlaslocal.go
  • modules/mongodb/atlaslocal/options.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3254
File: .github/dependabot.yml:21-21
Timestamp: 2025-09-18T08:24:27.458Z
Learning: In the testcontainers-go repository, submodules like atlaslocal that are part of a parent module (e.g., mongodb) share the same go.mod file and should not have separate Dependabot entries. They are already monitored through the parent module's Dependabot configuration entry.
📚 Learning: 2025-09-18T08:24:27.458Z
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3254
File: .github/dependabot.yml:21-21
Timestamp: 2025-09-18T08:24:27.458Z
Learning: In the testcontainers-go repository, submodules like atlaslocal that are part of a parent module (e.g., mongodb) share the same go.mod file and should not have separate Dependabot entries. They are already monitored through the parent module's Dependabot configuration entry.

Applied to files:

  • modules/mongodb/atlaslocal/atlaslocal_test.go
  • docs/modules/mongodb-atlaslocal.md
🧬 Code graph analysis (2)
modules/mongodb/mongodb.go (1)
wait/host_port.go (1)
  • ForListeningPort (67-69)
modules/mongodb/atlaslocal/atlaslocal_test.go (4)
modules/mongodb/atlaslocal/atlaslocal.go (2)
  • Run (23-57)
  • Container (17-20)
testing.go (1)
  • CleanupContainer (91-97)
options.go (1)
  • ContainerCustomizer (22-24)
modules/mongodb/atlaslocal/options.go (13)
  • WithUsername (159-167)
  • WithPassword (171-179)
  • WithUsernameFile (184-215)
  • WithPasswordFile (220-251)
  • WithNoTelemetry (255-261)
  • WithMongotLogFile (363-369)
  • WithMongotLogToStdout (343-349)
  • WithMongotLogToStderr (353-359)
  • WithRunnerLogFile (393-399)
  • WithRunnerLogToStdout (373-379)
  • WithRunnerLogToStderr (383-389)
  • WithInitDatabase (266-272)
  • WithInitScripts (278-339)
🪛 markdownlint-cli2 (0.17.2)
docs/modules/mongodb-atlaslocal.md

18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (1.25.x, modules/mongodb) / test: modules/mongodb/1.25.x
  • GitHub Check: test (1.24.x, modules/mongodb) / test: modules/mongodb/1.24.x
  • GitHub Check: Analyze (go)
🔇 Additional comments (5)
modules/mongodb/mongodb.go (4)

20-20: Good: centralized MongoDB port constant.

Replacing string literals with a single constant improves maintainability.


44-48: LGTM: consistent use of defaultPort in request and wait strategy.

This removes duplication and reduces drift risk.


122-123: LGTM: ConnectionString now uses defaultPort.

Consistent with the other changes.


20-20: No export needed — atlaslocal defines its own defaultPort.
modules/mongodb/atlaslocal/atlaslocal.go declares const defaultPort = "27017/tcp" (line 14) and uses it; modules/mongodb/mongodb.go has its own unexported defaultPort (line 20). No cross-package export or change required.

modules/mongodb/atlaslocal/atlaslocal_test.go (1)

185-189: Incorrect — repo targets Go 1.24; math/rand/v2 is supported.
go.mod declares "go 1.24.0"; modules/mongodb/atlaslocal/atlaslocal_test.go imports "math/rand/v2" and uses rand.Shuffle — no change required.

Likely an incorrect or invalid review comment.

Copy link
Member

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for your work here!

We really appreciate that Mongo engineers cooperate with the Testcontainers community adding the Atlas Local module, this is great for the OSS 💪

@mdelapenya mdelapenya merged commit 6e14bda into testcontainers:main Sep 18, 2025
17 checks passed
@mdelapenya
Copy link
Member

@prestonvasquez @matthewdale thanks again for your work here. If possible, checking that the mongodb module also follows the best practices recommended by the vendor would be really helpful 🙏

@mdelapenya
Copy link
Member

@prestonvasquez @matthewdale the module is present in our modules catalog here: https://testcontainers.com/modules/mongodb-atlas/?language=go

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New functionality or new behaviors on the existing one

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add mongodb-atlas-local module

3 participants