Skip to content

Conversation

@xytis
Copy link

@xytis xytis commented Jan 30, 2025

What does this PR do?

This module allows running firebase emulator as a testcontainer.

It does currently depend on a forked docker image, residing under my ownership. There is no official firebase emulator docker image to my knowledge.

Why is it important?

We use firebase, thought someone else might be using firebase too.

@xytis xytis requested a review from a team as a code owner January 30, 2025 18:46
@netlify
Copy link

netlify bot commented Jan 30, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit fd6035e
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-go/deploys/680b7dcc6dca280008bd401e
😎 Deploy Preview https://deploy-preview-2954--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 site configuration.

Copy link
Contributor

@stevenh stevenh left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, I've done a initial pass. The majority of comments center around bringing it inline with the latest template for modules in terms of style and correct behaviour e.g. handling non nil container with error.

// WithRoot sets the directory which is copied to the destination container as firebase root
func WithRoot(rootPath string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) error {
if !strings.HasSuffix(rootPath, "/firebase") {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Could you clarify why this requirement is needed?

Copy link
Author

Choose a reason for hiding this comment

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

This has been an issue a year ago. Mounting did not correctly work at that time, and if the source directory did not match the destination directory, mounting would nest the directory instead of mapping it directly.

I see that the issue has been fixed, removing.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, my bad. The issue still exits. I wrote a failing test to exhibit the issue.

Is there something incorrect with the way I copy over the configuration?

Also, I don't think that volume mount would work in this case, because firebase emulator creates a lot of trash data in the root catalog, which in some cases can even make testing flaky (especially if using DATA_DIRECTORY as a fixture).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll try to find some time to look. I had a some in progress work to fix up the copy behaviour which was odd.

If you have a test to exercise this that, would help alongside a description of the desired behaviour and what you're seeing instead.

return fmt.Sprintf("%s:%s", host, port.Port()), nil
}

func (c *FirebaseContainer) UIConnectionString(ctx context.Context) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: seems like lots of additional API surface area instead of just exposing ConnectionString, thoughts?

If not then we'd need comments for all the methods.

Copy link
Author

Choose a reason for hiding this comment

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

This one is a bit more tricky.

I would love to change all of these methods, and figure out a better way to configure the launched container.

Currently I ship a catalog which contains static predefined configuration for the emulator. These methods happen to match the port numbers defined in the said file.

But, in the tests that we use, we provide a completely different setup (because we don't want to boot the full emulation suite), and we have to match the port numbers in our config so that all of this works.

So a question from my side would be:
Is there a way to generate file based config during container setup? To my knowledge firebase emulator does not allow passing those values as environment variables.

I think, ideal use example would be this:

	firebaseContainer, err := firebase.Run(ctx, 
	  "ghcr.io/u-health/docker-firebase-emulator:13.29.2",
	  firebase.WithUI(),
	  firebase.WithFirestore(),
	  firebase.WithCache(),
	)

And then, helper methods XXXConnectionString would actually return meaningful errors if the emulator part was not launched.

Copy link
Member

Choose a reason for hiding this comment

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

Is there a way to generate file based config during container setup?

@xytis you can take a look at the redpanda module, where there is a Go template the module is using to build the configuration file.

Copy link
Author

Choose a reason for hiding this comment

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

After some testing on my side, I remembered why I shipped the config as a directory in the first place.

Default (and probably expected) use case for firebase projects, is to run firebase init and then use the scaffolded configuration to interact with the dependencies.

In the following commit, I've changed the behaviour of this module. It now attempts to understand the emulator configuration of your project and then boots the containerised emulator with that info.

At least in my use case, that makes sense, and when I launch the tests, I use the same project-wide config for all of them.

func (c *FirebaseContainer) connectionString(ctx context.Context, portName nat.Port) (string, error) {
host, err := c.Host(ctx)
if err != nil {
return "", err
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: wrap the error so the user knows the cause, more below.

Copy link
Contributor

@stevenh stevenh left a comment

Choose a reason for hiding this comment

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

Thanks for the updates

@xytis xytis force-pushed the main branch 2 times, most recently from 7404470 to cc2bfa4 Compare February 2, 2025 09:37
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.

I added a few minor comments, we are in the good way.

Thanks for your contribution


### Container Methods

The Firebase container exposes the following methods:
Copy link
Member

Choose a reason for hiding this comment

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

docs: we probably need to append here the new methods the container expose

Copy link
Member

Choose a reason for hiding this comment

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

@xytis let's not forget adding here ConnectionString 🙏

E.g. `Run(context.Background(), "ghcr.io/u-health/docker-firebase-emulator:13.29.2")`.

{% include "../features/common_functional_options.md" %}

Copy link
Member

Choose a reason for hiding this comment

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

docsd: we need to add here all the functional options for the module.

Copy link
Member

@mdelapenya mdelapenya Apr 22, 2025

Choose a reason for hiding this comment

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

@xytis let's not forget adding the WithData option here 🙏

@mdelapenya
Copy link
Member

One question: reading the README in the docker image:

Supports GRPC for firestore and pub/sub.

What are the differences with the existing Google Cloud emulators? We do have them under the gcloud module, and we currently plan to follow the same package structure as in the upcoming azure module (see #3008). Do you think this firebase has enough entity to be a separate module, or should it live alongside the other gcloud modules?

@xytis
Copy link
Author

xytis commented Mar 13, 2025

Last time I checked, gcloud emulators did not provide the full suite of emulations. I will check this again and come back to you.

@mdelapenya
Copy link
Member

@xytis we merged the layout revamp in the gcloud module. Do you think we can move this there?

@xytis
Copy link
Author

xytis commented Apr 3, 2025

Hey,

unfortunately not.

gcloud emulators do not include the following:

  • firebase auth
  • firebase storage (which is an access control layer on top of gcloud storage)
  • firebase pubsub (again, a layer on top, with some helpers)

I am not certain about the rest.

@mdelapenya
Copy link
Member

mdelapenya commented Apr 3, 2025

gcloud emulators do not include the following:

but we're going to use the ghcr.io/u-health/docker-firebase-emulator:13.29.2 Docker image, which has them. So I'd like to know if it's "semantically" correct to locate this code under the gcloud module, so it's distributed with all GCP services, or if on the contrary, it makes more sense to have it as a separate module.

I do not have an opinion on this, so as the author of the image, please feel free to be prescriptive about it. 🙏

@xytis
Copy link
Author

xytis commented Apr 3, 2025

Well, if I was searching for "firebase" emulators, I would not look under "gcloud". Mostly because the tooling and the documentation (both provided by google) are very different for these two products.

I think, having these two separate makes more sense as long as there are development differences between running gcloud and firebase executables.

@mdelapenya
Copy link
Member

LGTM then to continue as a separate module, thanks! 🙏

I can help you out rebasing this PR resolving the conflicts before moving on with the review

@xytis
Copy link
Author

xytis commented Apr 3, 2025

Please do, I am a bit occupied at the moment.

Thanks.

@mdelapenya
Copy link
Member

@xytis I cannot push to your branch because this PR was submitted from your main branch 😞

I've pushed it to my origin so that you can check and pick up the commits: https://github.com/testcontainers/testcontainers-go/compare/main...mdelapenya:testcontainers-go:firebase-module?expand=1

I still have doubts about the WithData option, probably as a result of being this one my first interaction with Firebase. Could you help me out in understanding its motivation? I'd need some tests in the code to verify it works as expected.

@xytis
Copy link
Author

xytis commented Apr 14, 2025

@mdelapenya merged your changes.

I was forced to use WithData because Firebase emulators expect a specific directory structure and files to exist in the "project root".

I toyed with providing structured config, but I am not using that in my own tests. It was easier to bootstrap a directory with a specific catalog structure, and I just clone that into the container.

Rough docs on this are here: https://firebase.google.com/docs/emulator-suite/install_and_configure

@mdelapenya
Copy link
Member

@xytis lint is complaining. Could you run make lint from the firebase module dir? 🙏


go 1.23.0

require (
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: just before submitting the changes, run go mod tidy from the module dir to make sure all dependencies are in sync with the core. Else we'll receive an extra PR from dependabot 😅

Copy link
Author

Choose a reason for hiding this comment

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

go mod tidy returned no changes. Was this expected?

Copy link
Member

Choose a reason for hiding this comment

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

Probably no changes are missing. I just want to double check we are not missing recent updates in the main branch.

@mdelapenya
Copy link
Member

@xytis for some reason, the lint still fails:

  Error: modules/firebase/modules/firebase/firebase_test.go:13:1: File is not properly formatted (gci)
  
  ^
  Error: modules/firebase/modules/firebase/options.go:7:1: File is not properly formatted (gofumpt)

Once fixed, and the docs are updated (see #2954 (comment) and #2954 (comment)), I think we are good to merge.

mdelapenya
mdelapenya previously approved these changes Apr 23, 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.

I'm approving this PR already, although there are some comments to be addressed before we merge it:

  • documentation adding the WithData options and the ConnectionString container method
  • Steven's suggestion regarding wrapping errors.

Other than that, LGTM, thanks so much for your patience during the review, we do appreciate your hard work with Testcontainers and the Firebase emulator. 🙇

func (c *Container) ConnectionString(ctx context.Context, portName nat.Port) (string, error) {
host, err := c.Host(ctx)
if err != nil {
return "", err
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return "", err
return "", fmt.Errorf("host: %w", err)

Copy link
Member

Choose a reason for hiding this comment

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

@xytis friendly ping! 🙏

Copy link
Author

Choose a reason for hiding this comment

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

I'll get to this once I have some time :)

Copy link
Member

Choose a reason for hiding this comment

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

I'm cutting a release today. Do you mind if, as this PR was sent from your main branch and I cannot contribute to it, I close this PR and submit another PR with your commits and my suggestions on top? Then I can incorporate this new module in today's release

Copy link
Author

Choose a reason for hiding this comment

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

Oh, sure. Do as you wish :)

Copy link
Author

Choose a reason for hiding this comment

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

Honestly, I am fine if you took the code and rebased into whatever location you need it to be :)

Just note that the issue which was blocking this module is still here:

I can not copy directories into container, if the source dir name and the dest dir name are not matching.

There is a test/example for that, but I placed an inverse condition for some reason.

I'll revert it now, so it would be clear why this is lagging for so long.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that limitation exists at the moment. My question now would be: why would the users of the firebase module like to provide a wrong dir name? I can see two types of users:

  1. developers of the firebase emulator, who would need different failure modes while developing it
  2. developers using the emulator, who would not need that particular failure mode.

I guess developers in group 1 would love to have multiple root dirs with different use cases. As a workaround I can think of a previous step that copies those dirs to a (t.TempDir() + "/firebase"), and then copy that temporary dir to the container. We can add now an issue to testcontainers to resolve the issue as soon as possible and remove that workaround right after it's released.

Of course, as a firebase noob, I'm probably missing other concerns you may have clear. Please let me know what you think about this 🙏

Copy link
Author

Choose a reason for hiding this comment

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

The only reason why someone would have issues with directory name is when you have different setups for different tests.

In that case, the workaround seems to be a good solution.

Copy link
Member

@mdelapenya mdelapenya Apr 25, 2025

Choose a reason for hiding this comment

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

Let's do this: let the functional option do that automatically, storing the temp root dir in the container so that we add a new Terminate function that: 1) removes the temp dir, and 2) calls container.Terminate. Please honor the Terminate signature

@stevenh @xytis thoughts?

Copy link
Contributor

@stevenh stevenh left a comment

Choose a reason for hiding this comment

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

Just a quick pass as I'm busy today.

Port uint16 `json:"port,omitempty"`
} `json:"tasks,omitempty"`
}
type partialFirebaseConfig struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: add a blank line between types, suspect it will fail linting without.

ctx := context.Background()

firebaseContainer, err := firebase.Run(ctx, "ghcr.io/u-health/docker-firebase-emulator:13.29.2",
firebase.WithRoot(filepath.Join("testdata", "firebase")),
Copy link
Contributor

Choose a reason for hiding this comment

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

info: using literal "testdata/firebase" works fine on all platforms as even though windows uses \ it also accepts /.

Comment on lines +49 to +51
host := hostF.String()
if host != "0.0.0.0" {
return nil, fmt.Errorf("config specified %s emulator host on non public ip: %s", name, host)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

question: this seems to limit host to 0.0.0.0, if so why have the field? that said I'm not sure this check is needed, do you have an example of why it is?

Copy link
Author

Choose a reason for hiding this comment

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

If the emulator config (which we found in the user provided directory) uses host value other than "0.0.0.0", it will launch that emulator listening on docker internal IP. So no traffic will be able to enter the emulator, leaving the users very much confused.

portF := emulator.FieldByName("Port")
websocketPortF := emulator.FieldByName("WebsocketPort")

if hostF != (reflect.Value{}) && !hostF.IsZero() {
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: use IsValid to simplify test, more below

Suggested change
if hostF != (reflect.Value{}) && !hostF.IsZero() {
if hostF.isValid() && !hostF.IsZero() {

if emulator.Kind() != reflect.Struct {
continue
}
name := v.Type().Field(i).Name
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: extract type lookup out of the loop as its invariant.

Comment on lines 100 to 108
bytes, err := io.ReadAll(cfg)
if err != nil {
return nil, fmt.Errorf("read firebase.json: %w", err)
}

var parsed partialFirebaseConfig
if err := json.Unmarshal(bytes, &parsed); err != nil {
return nil, fmt.Errorf("parse firebase.json: %w", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: use a stream reader to be more memory efficient, its a small benefit but good practice.

if err != nil {
return nil, fmt.Errorf("gather ports: %w", err)
}
req.ExposedPorts = expectedExposedPorts
Copy link
Contributor

Choose a reason for hiding this comment

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

question: should we wait for all ports instead of the fragile log wait?

Copy link
Author

Choose a reason for hiding this comment

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

I had the same idea, but now I don't remember why I reverted back to logs.

// What would be a solution here? Previously I just added a check that the root must
// end in "/firebase"... I could do the same.
testcontainers.CleanupContainer(t, ctr)
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

bug: this should fail, as I understand it this is down to how our copy works or doesn't. I have an unfinished PR in this area here.

I would change this to check for err as expected and mark the test as skip for now.

@mdelapenya
Copy link
Member

For reference, Google released an official image: firebase/firebase-tools#1644 (comment)

@xytis
Copy link
Author

xytis commented May 7, 2025

Ah, cool. I'll adapt to that container once I have some free time.

@mdelapenya
Copy link
Member

Fantastic! thanks for your time @xytis , we appreciate it!

Co-authored-by: Manuel de la Peña <[email protected]>
Co-authored-by: Steven Hartland <[email protected]>
@netlify
Copy link

netlify bot commented Jan 15, 2026

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit bf3aea5
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/6968fa02254af60008bf7241
😎 Deploy Preview https://deploy-preview-2954--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.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Firebase module enabling containerized Firebase emulator execution with configurable root paths, data directories, and persistent cache support.
  • Documentation

    • Added comprehensive Firebase module documentation including usage examples, configuration options, and integration guidance.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Introduces a new Firebase module for testcontainers-go, including container wrapper, configuration structures, customization options, integration tests, example code, test fixtures, and documentation. Enables launching Firebase emulator containers with configurable root paths, data directories, and caching.

Changes

Cohort / File(s) Summary
Core Module Implementation
modules/firebase/firebase.go, modules/firebase/config.go, modules/firebase/options.go, modules/firebase/ports.go
Implements Container type wrapping testcontainers.Container, defines Run() function for instantiating Firebase containers with dynamic port exposure via reflection-based config parsing, provides WithRoot/WithData/WithCache customization options, and ConnectionString helper method for port mapping
Testing & Examples
modules/firebase/firebase_test.go, modules/firebase/examples_test.go
Adds integration tests verifying Firebase container startup, Firestore client connectivity, CRUD operations, and root path requirement validation; includes example test demonstrating module usage
Build & Dependencies
modules/firebase/Makefile, modules/firebase/go.mod
Establishes module build rules and declares dependencies including cloud.google.com/go/firestore, testcontainers-go, and transitive indirect dependencies
Documentation & Configuration
.vscode/.testcontainers-go.code-workspace, docs/modules/firebase.md, mkdocs.yml
Adds Firebase module documentation covering Run function, options, and usage examples; updates VS Code workspace and mkdocs navigation to include Firebase module
Test Fixtures
modules/firebase/testdata/firebase/*, modules/firebase/testdata/failure/*
Provides Firebase configuration files (.firebaserc, firebase.json) and security rules (firestore.rules, storage.rules, firestore.indexes.json) for both success and failure test scenarios

Sequence Diagram(s)

sequenceDiagram
    participant Test
    participant Run as firebase.Run()
    participant Config as Config Parser
    participant TC as testcontainers
    participant Container as Firebase<br/>Container
    
    Test->>Run: Run(ctx, image, opts...)
    Run->>Config: Read & parse firebase.json
    Config-->>Run: emulatorsConfig
    Run->>Run: Gather enabled ports via reflection
    Run->>TC: Create GenericContainerRequest
    Run->>TC: Start container with ports
    TC->>Container: Initialize
    Container-->>TC: Running container
    TC-->>Run: testcontainers.Container
    Run-->>Test: *Container wrapper
Loading
sequenceDiagram
    participant Test
    participant Container as Container
    participant Firestore as Firestore Client
    participant Emulator as Firebase<br/>Emulator
    
    Test->>Container: ConnectionString(ctx, port)
    Container->>Container: Host(ctx) + MappedPort(ctx)
    Container-->>Test: "host:port"
    Test->>Firestore: Initialize with URL
    Test->>Emulator: Write document
    Emulator-->>Test: Success
    Test->>Emulator: Read document
    Emulator-->>Test: Document data
    Test->>Test: Assert value
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

feature

Suggested reviewers

  • mdelapenya
  • stevenh

Poem

🐰 A Firebase module hops into the fold,
With containers and emulators, a sight to behold,
Ports gathered with care, configs parsed with grace,
Testcontainers now has Firebase's embrace!
✨ Hop forward, dear module, with tests shining bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: added firebase module' clearly summarizes the main change: addition of a new Firebase module to testcontainers-go.
Description check ✅ Passed The PR description is directly related to the changeset, explaining what the Firebase module does, why it exists, and noting its dependency on a forked Docker image.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

🤖 Fix all issues with AI agents
In `@modules/firebase/firebase.go`:
- Line 94: Replace the use of path.Join when building the filesystem path for
opening firebase.json: change the import to use the standard library filepath
package and use filepath.Join(userRoot, "firebase.json") in the os.Open call
(the variables/functions to update are the import list, the path.Join reference
in the line where cfg is assigned, and any other occurrences using path.Join
with host filesystem paths like userRoot). Ensure no other code relies on path
(URL) semantics for these filesystem paths.

In `@modules/firebase/go.mod`:
- Line 73: The project currently pulls an insecure indirect dependency
golang.org/x/oauth2 v0.24.0 (via cloud.google.com/go/firestore) which has
CVE-2025-22868; update the indirect module to v0.27.0 or later by running the
module update (e.g., use go get -u golang.org/x/[email protected]) and then run go
mod tidy to refresh modules so go.mod/go.sum reflect the upgraded
golang.org/x/oauth2 version referenced by cloud.google.com/go/firestore.
♻️ Duplicate comments (1)
docs/modules/firebase.md (1)

68-70: Documentation incomplete: Container Methods section is empty.

The "Container Methods" section ends without listing any methods. Based on past review comments and the module implementation, the ConnectionString method should be documented here. Additionally, the WithData option mentioned in past reviews appears to be missing from the Container Options section.

🧹 Nitpick comments (8)
modules/firebase/ports.go (1)

10-22: Implementation is clean and follows testcontainers-go patterns.

Error handling is well-structured with descriptive wrapping, and the blank lines after error returns improve readability.

Consider adding a brief godoc comment for this exported method to document its purpose and the expected portName parameter:

📝 Suggested documentation
+// ConnectionString returns a connection string in the format "host:port" for the specified
+// emulator port. The portName should match one of the ports exposed by the Firebase emulator
+// configuration (e.g., "8080/tcp" for Firestore).
 func (c *Container) ConnectionString(ctx context.Context, portName nat.Port) (string, error) {
modules/firebase/options.go (2)

7-19: Consider removing unused Option type.

The Option type and options struct appear unused - Option implements ContainerCustomizer with a NOOP but isn't used by the actual option functions (WithRoot, WithData, WithCache), which return testcontainers.CustomizeRequestOption instead. If this scaffolding is for future use, consider adding a TODO comment explaining the intent.


41-51: Volume name prefix inconsistency: "firestore-cache" vs "firebase-cache".

The volume caches Firebase binaries generally (at /root/.cache/firebase), but the volume name uses "firestore-cache-" prefix. Consider renaming to "firebase-cache-" for consistency with the actual purpose.

Proposed fix
 func WithCache() testcontainers.CustomizeRequestOption {
-	volumeName := "firestore-cache-" + testcontainers.SessionID()
+	volumeName := "firebase-cache-" + testcontainers.SessionID()

 	return testcontainers.WithMounts(testcontainers.ContainerMount{
docs/modules/firebase.md (1)

13-15: Add language specifier to the fenced code block.

The code block should specify a language for proper syntax highlighting and to satisfy markdown linting rules.

Proposed fix
-```
+```bash
 go get github.com/testcontainers/testcontainers-go/modules/firebase
</details>

</blockquote></details>
<details>
<summary>modules/firebase/examples_test.go (1)</summary><blockquote>

`16-18`: **Consider using consistent Docker image versions across example and tests.**

The example uses image version `13.29.2` while `firebase_test.go` uses version `15.3.0`. Consider aligning these versions for consistency and to ensure the example demonstrates the tested configuration.

</blockquote></details>
<details>
<summary>modules/firebase/firebase_test.go (1)</summary><blockquote>

`52-66`: **Clarify the expected outcome when this test is re-enabled.**

The test is appropriately skipped pending an upstream fix. However, the assertion `require.NoError(t, err)` at line 65 contradicts the comment explaining that the test should demonstrate a failure case. When this test is eventually re-enabled, consider whether it should assert an error condition instead.

</blockquote></details>
<details>
<summary>modules/firebase/firebase.go (2)</summary><blockquote>

`38-60`: **Consider using `IsValid()` for reflect.Value checks.**

The pattern `fieldValue != (reflect.Value{})` works but `IsValid()` is more idiomatic for checking if a reflect.Value was obtained from a valid field lookup.

<details>
<summary>♻️ Suggested refactor</summary>

```diff
-		enabledF := emulator.FieldByName("Enabled")
-		if enabledF != (reflect.Value{}) && !enabledF.Bool() {
+		if enabledF := emulator.FieldByName("Enabled"); enabledF.IsValid() && !enabledF.Bool() {
 			continue
 		}

-		hostF := emulator.FieldByName("Host")
-		portF := emulator.FieldByName("Port")
-		websocketPortF := emulator.FieldByName("WebsocketPort")
-
-		if hostF != (reflect.Value{}) && !hostF.IsZero() {
+		if hostF := emulator.FieldByName("Host"); hostF.IsValid() && !hostF.IsZero() {
 			host := hostF.String()
 			if host != "0.0.0.0" {
 				return nil, fmt.Errorf("config specified %s emulator host on non public ip: %s", name, host)
 			}
 		}
-		if portF != (reflect.Value{}) && !portF.IsZero() {
+		if portF := emulator.FieldByName("Port"); portF.IsValid() && !portF.IsZero() {
 			port := fmt.Sprintf("%d/tcp", portF.Uint())
 			ports = append(ports, port)
 		}
-		if websocketPortF != (reflect.Value{}) && !websocketPortF.IsZero() {
+		if websocketPortF := emulator.FieldByName("WebsocketPort"); websocketPortF.IsValid() && !websocketPortF.IsZero() {
 			port := fmt.Sprintf("%d/tcp", websocketPortF.Uint())
 			ports = append(ports, port)
 		}

109-109: ExposedPorts assignment overwrites any user-specified ports.

If a customizer adds ports via req.ExposedPorts, this line replaces them entirely. Consider appending instead to preserve user-configured ports.

♻️ Suggested fix
-	req.ExposedPorts = expectedExposedPorts
+	req.ExposedPorts = append(req.ExposedPorts, expectedExposedPorts...)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72929c8 and bf3aea5.

⛔ Files ignored due to path filters (1)
  • modules/firebase/go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • .vscode/.testcontainers-go.code-workspace
  • docs/modules/firebase.md
  • mkdocs.yml
  • modules/firebase/Makefile
  • modules/firebase/config.go
  • modules/firebase/examples_test.go
  • modules/firebase/firebase.go
  • modules/firebase/firebase_test.go
  • modules/firebase/go.mod
  • modules/firebase/options.go
  • modules/firebase/ports.go
  • modules/firebase/testdata/failure/.firebaserc
  • modules/firebase/testdata/failure/firebase.json
  • modules/firebase/testdata/failure/firestore.indexes.json
  • modules/firebase/testdata/failure/firestore.rules
  • modules/firebase/testdata/failure/storage.rules
  • modules/firebase/testdata/firebase/.firebaserc
  • modules/firebase/testdata/firebase/firebase.json
  • modules/firebase/testdata/firebase/firestore.indexes.json
  • modules/firebase/testdata/firebase/firestore.rules
  • modules/firebase/testdata/firebase/storage.rules
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: natsoman
Repo: testcontainers/testcontainers-go PR: 3452
File: modules/cosmosdb/cosmosdb.go:11-14
Timestamp: 2025-11-05T11:57:09.224Z
Learning: In the testcontainers-go CosmosDB module (modules/azure/cosmosdb/), the defaultProtocol is intentionally set to "http" (not "https") to avoid requiring users to perform certificate setup steps (downloading and trusting the emulator's self-signed certificate). This design prioritizes ease of use for test containers over strict emulator fidelity.
📚 Learning: 2025-09-29T15:08:18.694Z
Learnt from: mdelapenya
Repo: testcontainers/testcontainers-go PR: 3320
File: modules/artemis/artemis.go:98-103
Timestamp: 2025-09-29T15:08:18.694Z
Learning: In testcontainers-go, nat.Port is a type alias for string, so untyped string constants can be passed directly to functions expecting nat.Port (like wait.ForListeningPort) without explicit type conversion - the Go compiler handles the implicit conversion automatically.

Applied to files:

  • modules/firebase/ports.go
📚 Learning: 2025-11-05T11:57:09.224Z
Learnt from: natsoman
Repo: testcontainers/testcontainers-go PR: 3452
File: modules/cosmosdb/cosmosdb.go:11-14
Timestamp: 2025-11-05T11:57:09.224Z
Learning: In the testcontainers-go CosmosDB module (modules/azure/cosmosdb/), the defaultProtocol is intentionally set to "http" (not "https") to avoid requiring users to perform certificate setup steps (downloading and trusting the emulator's self-signed certificate). This design prioritizes ease of use for test containers over strict emulator fidelity.

Applied to files:

  • modules/firebase/ports.go
  • modules/firebase/go.mod
📚 Learning: 2025-09-18T08:24:27.479Z
Learnt from: mdelapenya
Repo: testcontainers/testcontainers-go PR: 3254
File: .github/dependabot.yml:21-21
Timestamp: 2025-09-18T08:24:27.479Z
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:

  • docs/modules/firebase.md
  • modules/firebase/go.mod
  • .vscode/.testcontainers-go.code-workspace
📚 Learning: 2025-09-29T13:57:14.636Z
Learnt from: mdelapenya
Repo: testcontainers/testcontainers-go PR: 3319
File: modules/arangodb/arangodb.go:46-57
Timestamp: 2025-09-29T13:57:14.636Z
Learning: In testcontainers-go ArangoDB module, the wait strategy combines port listening check with HTTP readiness check using wait.ForAll - both strategies are required and complementary, not redundant.

Applied to files:

  • modules/firebase/go.mod
  • modules/firebase/firebase_test.go
🧬 Code graph analysis (4)
modules/firebase/ports.go (1)
modules/firebase/firebase.go (1)
  • Container (18-20)
modules/firebase/firebase.go (4)
options.go (1)
  • ContainerCustomizer (22-24)
generic.go (2)
  • GenericContainerRequest (21-27)
  • GenericContainer (52-98)
container.go (2)
  • ContainerRequest (131-171)
  • ContainerFile (110-115)
wait/log.go (1)
  • ForLog (118-120)
modules/firebase/options.go (4)
options.go (3)
  • ContainerCustomizer (22-24)
  • CustomizeRequestOption (28-28)
  • WithFiles (524-529)
generic.go (1)
  • GenericContainerRequest (21-27)
container.go (1)
  • ContainerFile (110-115)
mounts.go (1)
  • ContainerMount (133-140)
modules/firebase/examples_test.go (4)
modules/firebase/firebase.go (1)
  • Run (67-122)
modules/firebase/options.go (2)
  • WithRoot (22-28)
  • WithCache (42-51)
cleanup.go (1)
  • TerminateContainer (97-108)
log/logger.go (1)
  • Printf (47-49)
🪛 checkmake (0.2.2)
modules/firebase/Makefile

[warning] 3-3: Missing required phony target "all"

(minphony)


[warning] 3-3: Missing required phony target "clean"

(minphony)

🪛 markdownlint-cli2 (0.18.1)
docs/modules/firebase.md

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

(MD040, fenced-code-language)

🪛 OSV Scanner (2.3.1)
modules/firebase/go.mod

[HIGH] 1-1: golang.org/x/oauth2 0.24.0: Unexpected memory consumption during token parsing in golang.org/x/oauth2

(GO-2025-3488)


[HIGH] 1-1: golang.org/x/oauth2 0.24.0: golang.org/x/oauth2 Improper Validation of Syntactic Correctness of Input vulnerability

(GHSA-6v2p-p543-phr9)

🔇 Additional comments (23)
modules/firebase/testdata/failure/.firebaserc (1)

1-5: LGTM!

Standard Firebase project configuration file with appropriate test project name.

modules/firebase/testdata/failure/storage.rules (1)

1-8: LGTM!

Permissive storage rules are appropriate for test fixtures. The if true pattern provides unrestricted access needed for testing container functionality without security constraints.

modules/firebase/testdata/firebase/firestore.indexes.json (1)

1-4: LGTM!

Valid Firestore indexes configuration with empty arrays, appropriate for basic emulator test fixtures.

modules/firebase/testdata/firebase/.firebaserc (1)

1-5: LGTM!

Standard Firebase project configuration file with appropriate test project name.

modules/firebase/Makefile (1)

1-5: LGTM!

The Makefile follows the established pattern across all modules in the repository: include shared test infrastructure and delegate to module-specific targets.

modules/firebase/testdata/failure/firestore.rules (1)

1-8: LGTM!

Valid Firestore security rules with a deny-all policy, appropriate for a failure test scenario.

modules/firebase/testdata/firebase/firestore.rules (1)

1-8: LGTM!

Standard Firestore rules file with deny-all policy. Suitable as a test fixture for emulator configuration.

modules/firebase/testdata/firebase/storage.rules (1)

1-8: LGTM!

Permissive storage rules appropriate for test fixtures where storage operations should succeed.

modules/firebase/testdata/firebase/firebase.json (1)

1-39: Configuration looks good overall.

The emulator configuration correctly binds all services to 0.0.0.0 for container accessibility, and uses standard Firebase emulator ports. The inline comment about UI limitations in containers is helpful. The singleProjectMode setting is correctly placed inside the emulators object per Firebase configuration schema.

.vscode/.testcontainers-go.code-workspace (1)

92-95: LGTM!

The Firebase module entry is correctly added in alphabetical order and follows the established naming convention (module / firebase) consistent with other modules in the workspace.

modules/firebase/config.go (1)

1-87: LGTM!

The configuration structs are well-structured and appropriately model the Firebase emulator configuration. The types are correctly unexported for internal use, port fields use uint16, and JSON tags are properly applied with omitempty.

modules/firebase/go.mod (1)

1-12: LGTM on module structure.

The module declaration, Go version, and direct dependencies are appropriate for the Firebase module. The replace directive correctly points to the parent module for local development.

modules/firebase/testdata/failure/firestore.indexes.json (1)

1-4: LGTM!

Valid Firestore indexes configuration file for test fixtures.

modules/firebase/options.go (1)

32-37: The nil map concern does not apply to this code in the Firebase context.

Firebase's Run() function always initializes req.Env to an empty map at line 72 before applying any CustomizeRequestOption functions, so WithData will never encounter a nil map. The code is correct and does not need the proposed fix.

Likely an incorrect or invalid review comment.

mkdocs.yml (1)

95-95: LGTM!

The Firebase module documentation entry is correctly added in alphabetical order within the Modules navigation section.

modules/firebase/testdata/failure/firebase.json (1)

1-39: LGTM!

The Firebase emulator configuration for the failure test scenario is well-structured. The inline comment explaining UI limitations in containerized environments is helpful context.

modules/firebase/examples_test.go (1)

13-40: LGTM!

The example correctly demonstrates the Firebase module usage pattern:

  • Proper cleanup with deferred TerminateContainer before error check (handles nil container gracefully)
  • Uses log.Printf instead of log.Fatal to ensure defers run
  • Cross-platform path handling with filepath.Join
modules/firebase/firebase_test.go (2)

17-50: LGTM!

This integration test is well-structured:

  • Proper context with deadline and cancellation
  • Correct cleanup pattern with CleanupContainer before error assertion
  • Uses t.Setenv for environment variables (auto-restored after test)
  • Uses modern any type
  • Verifies end-to-end Firestore write/read cycle

68-75: LGTM!

Good negative test case that verifies Run returns ErrRootNotProvided when the required root option is missing. The error assertion with require.ErrorIs is the correct approach.

modules/firebase/firebase.go (4)

1-15: Imports look appropriate for the module's functionality.

The package structure and imports align with other testcontainers-go modules. One note: verify that path (line 9) vs path/filepath is intentional—see related comment on line 94.


17-25: LGTM!

The Container wrapper and sentinel error follow standard testcontainers-go module patterns.


66-90: LGTM!

The request setup follows testcontainers-go conventions. The log-based wait strategy is appropriate for detecting emulator readiness, and the root path validation ensures required configuration is present.


111-119: LGTM!

The error handling correctly returns the container even on failure, allowing callers to perform cleanup. This follows the testcontainers-go pattern for handling partial creation failures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


// Parse expected emulators from the root:
userRoot := req.Files[rootPathIdx].HostFilePath
cfg, err := os.Open(path.Join(userRoot, "firebase.json"))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use filepath.Join instead of path.Join for host filesystem paths.

path.Join is for URL-style forward-slash paths, while filepath.Join handles OS-specific path separators. On Windows, path.Join won't correctly handle backslashes in userRoot.

🔧 Proposed fix

Update the import:

-	"path"
+	"path/filepath"

And the usage:

-	cfg, err := os.Open(path.Join(userRoot, "firebase.json"))
+	cfg, err := os.Open(filepath.Join(userRoot, "firebase.json"))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cfg, err := os.Open(path.Join(userRoot, "firebase.json"))
cfg, err := os.Open(filepath.Join(userRoot, "firebase.json"))
🤖 Prompt for AI Agents
In `@modules/firebase/firebase.go` at line 94, Replace the use of path.Join when
building the filesystem path for opening firebase.json: change the import to use
the standard library filepath package and use filepath.Join(userRoot,
"firebase.json") in the os.Open call (the variables/functions to update are the
import list, the path.Join reference in the line where cfg is assigned, and any
other occurrences using path.Join with host filesystem paths like userRoot).
Ensure no other code relies on path (URL) semantics for these filesystem paths.

go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat modules/firebase/go.mod

Repository: testcontainers/testcontainers-go

Length of output: 4101


🌐 Web query:

golang.org/x/oauth2 GO-2025-3488 vulnerability

💡 Result:

Summary: GO-2025-3488 (CVE-2025-22868) is a high-severity bug in golang.org/x/oauth2 (package golang.org/x/oauth2/jws) where a specially crafted malformed token can cause unexpected memory consumption during parsing (potential DoS). Affects all versions before v0.27.0; fixed in v0.27.0. Recommended action: upgrade to golang.org/x/oauth2 v0.27.0 or later. [1][2][3][4]

References:

  • pkg.go.dev vulnerability entry (GO-2025-3488 / CVE-2025-22868). [1]
  • OSV / API entry for GO-2025-3488. [2]
  • GitHub Advisory (GHSA-6v2p-p543-phr9) / advisory details. [3]
  • Debian / security tracker entry (package fixes and status). [4]

🌐 Web query:

golang.org/x/oauth2 GHSA-6v2p-p543-phr9 security advisory

💡 Result:

Short summary:

  • GHSA-6v2p-p543-phr9 refers to golang.org/x/oauth2 (alias CVE-2025-22868 / GO-2025-3488). [1][2]
  • Affected versions: all releases before v0.27.0. Patched in v0.27.0. [1][2]
  • Impact: malformed tokens can trigger unexpected memory consumption during parsing (DoS risk). Severity: High (CVSS 7.5). [1][3]
  • Mitigation: upgrade to v0.27.0 or later (e.g. run: go get golang.org/x/[email protected]). [2][3]

Sources: GitHub Advisory (GHSA-6v2p-p543-phr9). [1] OSV / pkg.go.dev (GO-2025-3488 / CVE-2025-22868). [2] Vendor/analysis summaries (Wiz/NVD references). [3]


🌐 Web query:

golang.org/x/oauth2 v0.24.0 latest version 2025

💡 Result:

v0.24.0 is not the latest. The module has been updated; the newest tagged release is v0.30.0. [1][2]

Sources:

  • go.googlesource.com/oauth2 — tags listing showing v0.30.0. [1]
  • go.googlesource.com/oauth2 — refs/tags/v0.24.0. [2]

Security vulnerability in indirect dependency golang.org/x/oauth2 v0.24.0.

Static analysis detected HIGH severity vulnerability GO-2025-3488 (CVE-2025-22868 / GHSA-6v2p-p543-phr9) in golang.org/x/oauth2 v0.24.0: malformed tokens can cause unexpected memory consumption during parsing, leading to DoS risk.

This is an indirect dependency pulled in via cloud.google.com/go/firestore. Upgrade to v0.27.0 or later by running go get -u golang.org/x/[email protected] followed by go mod tidy.

🤖 Prompt for AI Agents
In `@modules/firebase/go.mod` at line 73, The project currently pulls an insecure
indirect dependency golang.org/x/oauth2 v0.24.0 (via
cloud.google.com/go/firestore) which has CVE-2025-22868; update the indirect
module to v0.27.0 or later by running the module update (e.g., use go get -u
golang.org/x/[email protected]) and then run go mod tidy to refresh modules so
go.mod/go.sum reflect the upgraded golang.org/x/oauth2 version referenced by
cloud.google.com/go/firestore.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants