Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat: reuse PostgreSQL container across tests#3018

Merged
DanCech merged 8 commits into
openfga:mainopenfga/openfga:mainfrom
rafanaskin:reuse-pg-in-testsrafanaskin/openfga:reuse-pg-in-testsCopy head branch name to clipboard
Apr 17, 2026
Merged

feat: reuse PostgreSQL container across tests#3018
DanCech merged 8 commits into
openfga:mainopenfga/openfga:mainfrom
rafanaskin:reuse-pg-in-testsrafanaskin/openfga:reuse-pg-in-testsCopy head branch name to clipboard

Conversation

@rafanaskin

@rafanaskin rafanaskin commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Description

What problem is being solved?

The RunDatastoreTestContainer storage fixture currently creates a new container on each invocation, which can result in more than 10 containers being started during a single test run per storage. Each container initialization involves startup and running migrations, increasing execution time and resource usage in CI. This added overhead can negatively impact overall test performance.

How is it being solved?

I started with PostgreSQL by updating its test fixture to reuse a single container across tests, while maintaining isolation through per-test databases. This significantly reduces the overhead of container creation and migration execution.

As a result, the execution time of storage/postgres/postgres_test.go decreased by ~30%:

  • Before: github.com/openfga/openfga/pkg/storage/postgres 159.219s
  • After: github.com/openfga/openfga/pkg/storage/postgres 108.411s

Warning

The current implementation does not remove the PostgreSQL container after tests complete. Instead, it is cleaned up along with the CI worker. As an improvement, this cleanup step could be added to the make test command.

What changes are made to solve it?

  • Added an improved Docker client in testutils, used by the PostgreSQL fixture and ready for MySQL reuse.
  • Updated the PostgreSQL test fixture to reuse a single container.

References

followup #2912

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • Chores
    • Tests now reuse a shared PostgreSQL container and create per-test databases, speeding runs and reducing resource use.
  • Tests
    • Added a Docker client wrapper with comprehensive container lifecycle tests and updated test harness to use a parallel-ready default config.
  • Refactor
    • Improved database readiness, retry/backoff, replica sync, and migration/version wait logic to stabilize test startup.
  • Documentation
    • CHANGELOG updated describing the testing infrastructure changes.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replace per-test Postgres containers with a shared Docker Postgres instance and per-test databases; add a DockerClient wrapper and tests; introduce DB readiness and replica-sync helpers; initialize Goose migrations at package init; and adjust Postgres ping backoff construction.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Add note that tests now reuse a single PostgreSQL container across the test suite.
Postgres storage
pkg/storage/postgres/postgres.go
Refactor backoff construction for db.Ping retry to set MaxElapsedTime at creation time.
Postgres test fixture
pkg/testfixtures/storage/postgres.go
Rework fixture to discover/reuse or bootstrap a shared postgres:17-alpine container, use per-test randomized database names, add postgresConnectionURI, exported RunPostgresTestContainer(t), create/drop per-test DB, simplify replica creation, and move replica readiness logic to a new helper.
Readiness helpers
pkg/testfixtures/storage/readiness.go
New helpers waitForDatabase(driverName, uri) and waitForMigrationVersion(driverName, uri, expectedVersion) implementing connection and migration-version polling with backoff.
Test storage & migrations
pkg/testfixtures/storage/storage.go
Centralize Goose init in init() (nop logger, embedded migrations), route Postgres to shared runner, and add latestMigrationVersion helper.
Docker test utilities
pkg/testutils/dockerclient.go, pkg/testutils/dockerclient_test.go
Add testutils.DockerClient wrapper (New/Close) with PullImage, FindRunningContainer, RunContainer, ExecCommand, GetHostPort, RemoveContainer and extensive unit/integration-style tests including an HTTP mock harness.
Sqlite fixture
pkg/testfixtures/storage/sqlite.go
Remove per-call Goose logger/base FS setup (Goose init centralized).
Tests config
tests/tests.go
BuildClientInterface now uses testutils.MustDefaultConfigForParallelTests() and no longer forces RequestTimeout.

Sequence Diagram(s)

sequenceDiagram
  participant Test as Test code
  participant Fixture as Postgres fixture
  participant DockerClient as testutils.DockerClient
  participant DockerEngine as Docker daemon
  participant Postgres as Postgres container
  participant DB as Per-test database

  Test->>Fixture: RunPostgresTestContainer(t)
  Fixture->>DockerClient: FindRunningContainer(name, image)
  DockerClient->>DockerEngine: List/Inspect containers
  alt container found
    DockerEngine-->>DockerClient: InspectResponse
  else not found
    DockerClient->>DockerEngine: Pull image
    DockerClient->>DockerEngine: Create & Start container
    DockerEngine-->>DockerClient: InspectResponse
  end
  DockerClient-->>Fixture: host, port
  Fixture->>Postgres: ExecCommand(createdb per-test DB)
  Fixture->>DB: waitForDatabase(uri)
  Fixture->>DB: waitForMigrationVersion(uri)
  alt replica requested
    Fixture->>DockerClient: RunContainer(replica)
    Fixture->>Postgres: waitForPostgresReplicaSync(replicaURI)
  end
  Test->>Fixture: cleanup per-test DB (DROP DATABASE)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • adriantam
  • senojj

Poem

🐇 I hopped into Docker and fetched an alpine meal,
One shared Postgres garden grows, each test a tiny peel.
Migrations hum in order, replicas learn to queue,
I plant a database seed, then drop it when I'm through.
Carrot celebration done — tests run light and true.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% 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 title 'feat: reuse PostgreSQL container across tests' clearly and concisely describes the main objective of the pull request—enabling container reuse across tests rather than creating new containers for each invocation.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov

codecov Bot commented Mar 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.17476% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.81%. Comparing base (c8a0e7b) to head (7bbba5a).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
pkg/testutils/dockerclient.go 94.12% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3018      +/-   ##
==========================================
- Coverage   90.90%   90.81%   -0.08%     
==========================================
  Files         193      194       +1     
  Lines       21348    21449     +101     
==========================================
+ Hits        19404    19477      +73     
- Misses       1287     1305      +18     
- Partials      657      667      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rafanaskin
rafanaskin force-pushed the reuse-pg-in-tests branch 17 times, most recently from 34f9d0c to 94793b9 Compare April 1, 2026 20:39
@rafanaskin rafanaskin changed the title Reuse pg in tests feat: reuse PostgreSQL container across tests Apr 1, 2026
@rafanaskin
rafanaskin marked this pull request as ready for review April 1, 2026 23:19
@rafanaskin
rafanaskin requested review from a team as code owners April 1, 2026 23:19
Copilot AI review requested due to automatic review settings April 1, 2026 23:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves test infrastructure performance by reusing a single PostgreSQL container across tests instead of creating new containers for each test invocation. The implementation uses per-test databases within the shared container to maintain isolation, achieving approximately 30% reduction in test execution time for PostgreSQL tests.

Changes:

  • Added DockerClient wrapper utility in testutils for managing Docker containers and images
  • Refactored PostgreSQL test fixture to implement container reuse with per-test database isolation
  • Added waitForDatabase helper function to handle database readiness checks
  • Updated test container invocation from method-based to function-based approach

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/testutils/dockerclient.go New Docker client wrapper with container/image management utilities
pkg/testutils/dockerclient_test.go Comprehensive tests for Docker client functionality
pkg/testfixtures/storage/postgres.go Refactored to reuse containers and maintain per-test database isolation
pkg/testfixtures/storage/readiness.go New helper for database connectivity checks with backoff retry
pkg/testfixtures/storage/storage.go Updated postgres container invocation to use new function-based API
pkg/storage/postgres/postgres.go Minor refactoring of backoff policy initialization
CHANGELOG.md Documented performance improvement in unreleased section

Comment thread pkg/testfixtures/storage/postgres.go
Comment thread pkg/testfixtures/storage/readiness.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
pkg/testfixtures/storage/postgres.go (2)

130-138: Template database readiness depends on bootstrap completion.

The waitForDatabase on Line 131 only verifies TCP connectivity to postgresTemplateDB, but doesn't guarantee that migrations have completed. If a parallel test process finds the container mid-bootstrap (after container start but before goose.Up completes at Line 222), the createdb will succeed but the template may lack schema, causing subsequent migration version checks to fail.

Consider having the bootstrap process create a sentinel (e.g., a marker table or file) after migrations complete, and have consumers wait for that sentinel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/postgres.go` around lines 130 - 138, The current
readiness check uses waitForDatabase(...) which only verifies TCP/connectivity
to postgresTemplateDB before running createdb; instead add a post-bootstrap
sentinel (e.g., a marker table or file) created after migrations complete in the
bootstrap path (where goose.Up runs) and replace or augment the waitForDatabase
call in this setup flow with a new waitForTemplateReady (or waitForSentinel)
call that polls the sentinel via the same connection URI (use
testCont.GetConnectionURI(true)) before running container.ExecOptions createdb;
ensure the bootstrap code that runs goose.Up creates the sentinel and the test
fixture uses that sentinel check so createdb only proceeds after migrations
finish.

112-117: Potential race condition when multiple test binaries run in parallel.

When multiple test processes (e.g., different packages running with go test ./...) execute simultaneously, they may all observe "not found" from FindRunningContainer and attempt to bootstrap containers concurrently. While each container gets a unique ULID suffix preventing name collisions, this defeats the container reuse goal and wastes resources.

Consider adding a file-based lock or using Docker's container creation idempotently by catching "container already exists" errors and retrying the find operation.

💡 Suggested approach
 	dockerCont, found, err := docker.FindRunningContainer(t.Context(), "^"+postgresContainerPrefix, postgresImage)
 	require.NoError(t, err, "find running postgres container")

 	if !found {
-		dockerCont = bootstrapPostgresContainer(t, docker)
+		dockerCont, err = bootstrapPostgresContainerWithRetry(t, docker)
+		require.NoError(t, err, "bootstrap postgres container")
 	}

Where bootstrapPostgresContainerWithRetry catches "container name conflict" errors and re-checks for an existing container.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/postgres.go` around lines 112 - 117, The current
logic using docker.FindRunningContainer and bootstrapPostgresContainer can race
when multiple test processes see "not found" and all attempt to create
containers; update the flow by introducing a retry-or-lock around container
creation: implement a wrapper (e.g., bootstrapPostgresContainerWithRetry) that
first tries docker.FindRunningContainer(t.Context(),
"^"+postgresContainerPrefix, postgresImage), and if not found attempts
bootstrapPostgresContainer but catches Docker "container already exists" / name
conflict errors, then re-runs FindRunningContainer (with a short backoff) before
failing; alternatively use a simple file-based lock around the create path to
ensure only one process calls bootstrapPostgresContainer at a time—apply this
change where dockerCont is set so container creation becomes idempotent and
concurrent test runs reuse an existing container.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/testfixtures/storage/postgres.go`:
- Around line 130-138: The current readiness check uses waitForDatabase(...)
which only verifies TCP/connectivity to postgresTemplateDB before running
createdb; instead add a post-bootstrap sentinel (e.g., a marker table or file)
created after migrations complete in the bootstrap path (where goose.Up runs)
and replace or augment the waitForDatabase call in this setup flow with a new
waitForTemplateReady (or waitForSentinel) call that polls the sentinel via the
same connection URI (use testCont.GetConnectionURI(true)) before running
container.ExecOptions createdb; ensure the bootstrap code that runs goose.Up
creates the sentinel and the test fixture uses that sentinel check so createdb
only proceeds after migrations finish.
- Around line 112-117: The current logic using docker.FindRunningContainer and
bootstrapPostgresContainer can race when multiple test processes see "not found"
and all attempt to create containers; update the flow by introducing a
retry-or-lock around container creation: implement a wrapper (e.g.,
bootstrapPostgresContainerWithRetry) that first tries
docker.FindRunningContainer(t.Context(), "^"+postgresContainerPrefix,
postgresImage), and if not found attempts bootstrapPostgresContainer but catches
Docker "container already exists" / name conflict errors, then re-runs
FindRunningContainer (with a short backoff) before failing; alternatively use a
simple file-based lock around the create path to ensure only one process calls
bootstrapPostgresContainer at a time—apply this change where dockerCont is set
so container creation becomes idempotent and concurrent test runs reuse an
existing container.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b94326f-2d30-4e55-84ee-1f1edab733ff

📥 Commits

Reviewing files that changed from the base of the PR and between 342a783 and f691653.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/storage.go
  • pkg/testutils/dockerclient.go
  • pkg/testutils/dockerclient_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/testfixtures/storage/postgres.go (2)

228-234: Consider passing DockerClient as parameter instead of creating a new one.

A new DockerClient is created for each replica, though the caller already has one. This is minor for test code but could be cleaner.

♻️ Optional: Pass existing client
-func runPostgresReplica(t testing.TB, primary *postgresTestContainer) *postgresReplicaContainer {
-	docker, err := testutils.NewDockerClient()
-	require.NoError(t, err)
-
-	t.Cleanup(func() {
-		docker.Close()
-	})
+func runPostgresReplica(t testing.TB, docker *testutils.DockerClient, primary *postgresTestContainer) *postgresReplicaContainer {

Then update CreateSecondary to pass the client.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/postgres.go` around lines 228 - 234,
runPostgresReplica creates a new DockerClient via testutils.NewDockerClient for
each replica even though the caller already has one; change runPostgresReplica
to accept a DockerClient parameter and reuse it instead of calling
testutils.NewDockerClient, and update CreateSecondary (and any call sites
constructing postgresReplicaContainer) to accept and forward that DockerClient
so the same client instance is reused when creating
postgresReplicaContainer/secondary replicas.

352-360: Use sql.NullString.Valid for clearer NULL handling.

The pg_last_wal_receive_lsn() function returns NULL when the replica hasn't received any WAL. While checking String == "" works because sql.NullString sets String to empty when Valid is false, it's more semantically correct to check !Valid.

♻️ Clearer NULL check
-	if replicaLSN.String == "" {
+	if !replicaLSN.Valid || replicaLSN.String == "" {
 		return fmt.Errorf("replica has not received any WAL yet")
 	}
 
-	t.Logf("Replica is synchronized and receiving WAL at LSN: %s", replicaLSN.String)
+	t.Logf("Replica is synchronized and receiving WAL at LSN: %s", replicaLSN.String)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/postgres.go` around lines 352 - 360, Replace the
empty-string check on the sql.NullString with a semantic NULL check: after
scanning into replicaLSN (the sql.NullString returned from db.QueryRow("SELECT
pg_last_wal_receive_lsn()").Scan(&replicaLSN)), use replicaLSN.Valid (i.e., if
!replicaLSN.Valid) to detect the NULL/absence of WAL instead of comparing
replicaLSN.String == ""; keep the same error message and return path when the
value is not valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/testutils/dockerclient_test.go`:
- Line 67: Rename the test function TestContainerLifecycle_PoolAndExec to
TestContainerLifecycle_PullAndExec to fix the typo; update the function
declaration and any references or test invocations that use
TestContainerLifecycle_PoolAndExec so they point to
TestContainerLifecycle_PullAndExec (search for the symbol
TestContainerLifecycle_PoolAndExec and replace it accordingly).

---

Nitpick comments:
In `@pkg/testfixtures/storage/postgres.go`:
- Around line 228-234: runPostgresReplica creates a new DockerClient via
testutils.NewDockerClient for each replica even though the caller already has
one; change runPostgresReplica to accept a DockerClient parameter and reuse it
instead of calling testutils.NewDockerClient, and update CreateSecondary (and
any call sites constructing postgresReplicaContainer) to accept and forward that
DockerClient so the same client instance is reused when creating
postgresReplicaContainer/secondary replicas.
- Around line 352-360: Replace the empty-string check on the sql.NullString with
a semantic NULL check: after scanning into replicaLSN (the sql.NullString
returned from db.QueryRow("SELECT
pg_last_wal_receive_lsn()").Scan(&replicaLSN)), use replicaLSN.Valid (i.e., if
!replicaLSN.Valid) to detect the NULL/absence of WAL instead of comparing
replicaLSN.String == ""; keep the same error message and return path when the
value is not valid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4fc21e6a-ce00-4fdd-9528-59c9559ac583

📥 Commits

Reviewing files that changed from the base of the PR and between f691653 and 7c35a5d.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/storage.go
  • pkg/testutils/dockerclient.go
  • pkg/testutils/dockerclient_test.go
✅ Files skipped from review due to trivial changes (3)
  • pkg/testfixtures/storage/storage.go
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/testfixtures/storage/readiness.go
  • pkg/testutils/dockerclient.go

Comment thread pkg/testutils/dockerclient_test.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/testfixtures/storage/storage.go`:
- Around line 89-92: The goose.CollectMigrations call returns an error that is
currently returned raw; update the error return to wrap the original error with
context using fmt.Errorf so callers know which migrationDir failed (e.g., return
0, fmt.Errorf("collecting migrations from %s: %w", migrationDir, err)). Import
fmt if necessary and update the return in the block that calls
goose.CollectMigrations to use the %w verb.

In `@pkg/testutils/dockerclient.go`:
- Around line 33-36: The Close method on DockerClient currently returns the raw
error from d.client.Close(), losing context; update DockerClient.Close to call
d.client.Close(), check the returned error, and if non-nil wrap it using
fmt.Errorf with %w (for example: fmt.Errorf("DockerClient.Close: %w", err))
before returning so callers receive contextualized errors. Ensure fmt is
imported if not already.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f30c4f1-b639-4593-b1a1-7f432898a82a

📥 Commits

Reviewing files that changed from the base of the PR and between d4031f3 and d2016c7.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/sqlite.go
  • pkg/testfixtures/storage/storage.go
  • pkg/testutils/dockerclient.go
  • pkg/testutils/dockerclient_test.go
  • tests/tests.go
💤 Files with no reviewable changes (1)
  • pkg/testfixtures/storage/sqlite.go
✅ Files skipped from review due to trivial changes (3)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testutils/dockerclient_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/tests.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/postgres.go

Comment thread pkg/testfixtures/storage/storage.go
Comment thread pkg/testutils/dockerclient.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
pkg/testfixtures/storage/storage.go (1)

13-16: Consider removing redundant Goose setup in mysql.go.

The init() centralizes Goose configuration, but pkg/testfixtures/storage/mysql.go still calls goose.SetLogger() at line 140 and goose.SetBaseFS() at line 158, which override this global state. While functionally harmless (they set the same values), removing the redundant calls would improve consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/storage.go` around lines 13 - 16, Remove the
redundant Goose setup in mysql.go by deleting the extra calls to
goose.SetLogger(...) and goose.SetBaseFS(...) so the global initialization in
init() within storage.go is the single source of truth; locate and remove the
goose.SetLogger invocation (around the call in mysql.go) and the goose.SetBaseFS
invocation (the later call in mysql.go) so they no longer override the central
init() configuration.
pkg/testfixtures/storage/postgres.go (1)

126-126: Consider sanitizing the database name for SQL safety.

The database name is constructed using postgresDBPrefix + ulid.Make().String(). While ULIDs are alphanumeric and safe, the DROP DATABASE statement at line 145 uses string interpolation with fmt.Sprintf. If the prefix or format ever changes, this could become an injection risk.

The current implementation is safe because ULID characters are [0-9A-Z], but consider using pq.QuoteIdentifier or similar for defense-in-depth.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testfixtures/storage/postgres.go` at line 126, The database name is built
as postgresDBPrefix + ulid.Make().String() and later interpolated into a DROP
DATABASE string using fmt.Sprintf, so update the code to quote/sanitize the
identifier before interpolation: import and use pq.QuoteIdentifier (or an
equivalent identifier-quoting helper) to wrap the composed database name when
constructing any SQL that embeds it (e.g., the DROP DATABASE fmt.Sprintf call
and any other ad-hoc SQL), and keep the original composed name for internal
tracking; reference postgresDBPrefix, ulid.Make().String(), and the DROP
DATABASE fmt.Sprintf usage to locate where to apply pq.QuoteIdentifier.
pkg/testutils/dockerclient.go (1)

119-122: Port readiness timeout may be too aggressive compared to other test infrastructure timeouts.

The 5-second MaxElapsedTime here is significantly shorter than waitForDatabase (30s) and waitForMigrationVersion (1 minute) in pkg/testfixtures/storage/readiness.go. While Docker port binding is typically a fast local operation, slow CI environments may hit this limit. Consider either increasing the timeout or documenting why Docker port binding requires a shorter window than database/migration operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testutils/dockerclient.go` around lines 119 - 122, The current backoff
policy created by backoff.NewExponentialBackOff sets MaxElapsedTime to
5*time.Second via the backoffPolicy variable which is much shorter than other
test readiness timeouts (waitForDatabase 30s and waitForMigrationVersion 1m);
update the MaxElapsedTime to a longer duration (e.g., 30*time.Second) or make it
configurable so Docker port readiness aligns with CI flakiness expectations, and
adjust the call site that uses backoffPolicy accordingly to use the new value or
configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/testfixtures/storage/postgres.go`:
- Line 126: The database name is built as postgresDBPrefix +
ulid.Make().String() and later interpolated into a DROP DATABASE string using
fmt.Sprintf, so update the code to quote/sanitize the identifier before
interpolation: import and use pq.QuoteIdentifier (or an equivalent
identifier-quoting helper) to wrap the composed database name when constructing
any SQL that embeds it (e.g., the DROP DATABASE fmt.Sprintf call and any other
ad-hoc SQL), and keep the original composed name for internal tracking;
reference postgresDBPrefix, ulid.Make().String(), and the DROP DATABASE
fmt.Sprintf usage to locate where to apply pq.QuoteIdentifier.

In `@pkg/testfixtures/storage/storage.go`:
- Around line 13-16: Remove the redundant Goose setup in mysql.go by deleting
the extra calls to goose.SetLogger(...) and goose.SetBaseFS(...) so the global
initialization in init() within storage.go is the single source of truth; locate
and remove the goose.SetLogger invocation (around the call in mysql.go) and the
goose.SetBaseFS invocation (the later call in mysql.go) so they no longer
override the central init() configuration.

In `@pkg/testutils/dockerclient.go`:
- Around line 119-122: The current backoff policy created by
backoff.NewExponentialBackOff sets MaxElapsedTime to 5*time.Second via the
backoffPolicy variable which is much shorter than other test readiness timeouts
(waitForDatabase 30s and waitForMigrationVersion 1m); update the MaxElapsedTime
to a longer duration (e.g., 30*time.Second) or make it configurable so Docker
port readiness aligns with CI flakiness expectations, and adjust the call site
that uses backoffPolicy accordingly to use the new value or configuration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a079362-728c-4784-8717-b967aa26299d

📥 Commits

Reviewing files that changed from the base of the PR and between d2016c7 and 643f2ca.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/sqlite.go
  • pkg/testfixtures/storage/storage.go
  • pkg/testutils/dockerclient.go
  • pkg/testutils/dockerclient_test.go
  • tests/tests.go
💤 Files with no reviewable changes (1)
  • pkg/testfixtures/storage/sqlite.go
✅ Files skipped from review due to trivial changes (2)
  • pkg/storage/postgres/postgres.go
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/tests.go
  • pkg/testfixtures/storage/readiness.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/testutils/dockerclient_test.go (1)

101-135: Assert the actual ContainerList filters in these cases.

These FindRunningContainer tests only validate method/path, so they still go green if the request is built with the wrong name / ancestor / status filters. Since the shared-container lookup depends on those arguments, I'd add a per-step assertion hook and validate the serialized request in the /containers/json cases.

🧪 Minimal hook to let mock steps validate request details
 type dockerMockStep struct {
 	Method string
 	Path   string
 	Body   []byte
 	Error  string
+	Assert func(*testing.T, *http.Request)
 }
@@
 		if !strings.HasSuffix(r.URL.Path, step.Path) {
 			t.Fatalf("unexpected path %s", r.URL.Path)
 		}
+		if step.Assert != nil {
+			step.Assert(t, r)
+		}
 
 		if step.Body != nil {
 			_, _ = w.Write(step.Body)
 			return

Also applies to: 360-374

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/testutils/dockerclient_test.go` around lines 101 - 135, The tests for
TestFindRunningContainer_Fail call FindRunningContainer but only assert HTTP
method/path; add a validation hook to the docker mock (via dockerMockStep or
newDockerClientMock) so that when a step with Path "/containers/json" is invoked
it decodes the actual request query/body into the Docker ContainerList filters
and asserts the expected name, ancestor (alpineImage) and status filters are
present; update the three subtests ("not_found", "list_containers",
"inspect_container") to supply that assertion hook for the /containers/json step
(and mirror the same change in the other failing test range referenced) so the
mock fails if the request is built with wrong filters while leaving existing
method/path checks intact and keeping FindRunningContainer, newDockerClientMock
and dockerMockStep usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/testfixtures/storage/readiness.go`:
- Around line 26-27: The retry uses db.Ping which has no deadline; change the
retry body to call db.PingContext with a per-attempt context timeout (e.g., 2s)
so each attempt fails fast. Replace the backoff.Retry(db.Ping, backoffPolicy)
call with a closure that creates a context via context.WithTimeout, defers
cancel, calls db.PingContext(ctx) and returns that error to Retry (keep
backoffPolicy unchanged). Ensure imports include context if missing.

---

Nitpick comments:
In `@pkg/testutils/dockerclient_test.go`:
- Around line 101-135: The tests for TestFindRunningContainer_Fail call
FindRunningContainer but only assert HTTP method/path; add a validation hook to
the docker mock (via dockerMockStep or newDockerClientMock) so that when a step
with Path "/containers/json" is invoked it decodes the actual request query/body
into the Docker ContainerList filters and asserts the expected name, ancestor
(alpineImage) and status filters are present; update the three subtests
("not_found", "list_containers", "inspect_container") to supply that assertion
hook for the /containers/json step (and mirror the same change in the other
failing test range referenced) so the mock fails if the request is built with
wrong filters while leaving existing method/path checks intact and keeping
FindRunningContainer, newDockerClientMock and dockerMockStep usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: da476513-1b26-4015-8f43-bd37ddcf3a14

📥 Commits

Reviewing files that changed from the base of the PR and between 643f2ca and d3893db.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • pkg/storage/postgres/postgres.go
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/readiness.go
  • pkg/testfixtures/storage/sqlite.go
  • pkg/testfixtures/storage/storage.go
  • pkg/testutils/dockerclient.go
  • pkg/testutils/dockerclient_test.go
  • tests/tests.go
💤 Files with no reviewable changes (1)
  • pkg/testfixtures/storage/sqlite.go
✅ Files skipped from review due to trivial changes (2)
  • pkg/storage/postgres/postgres.go
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/testfixtures/storage/postgres.go
  • pkg/testfixtures/storage/storage.go

Comment thread pkg/testfixtures/storage/readiness.go Outdated
Comment thread pkg/storage/postgres/postgres.go
Comment thread pkg/testfixtures/storage/postgres.go Outdated
@rafanaskin
rafanaskin force-pushed the reuse-pg-in-tests branch 2 times, most recently from 77a9b65 to 5fd16dd Compare April 14, 2026 17:02
Added an improved Docker client in testutils, used by the PostgreSQL fixture and ready for MySQL reuse.

Updated the PostgreSQL test fixture to reuse a single container while isolating tests via per-test databases,
reducing storage/postgres/postgres_test.go execution time by ~30%.
@rafanaskin
rafanaskin force-pushed the reuse-pg-in-tests branch 2 times, most recently from 8e1f771 to ebaf7d5 Compare April 16, 2026 17:06
Comment thread Makefile Outdated
senojj
senojj previously approved these changes Apr 17, 2026

@senojj senojj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I approve these changes, but would also like for @DanCech to sign off before merging.

Comment thread pkg/testfixtures/storage/postgres.go Outdated

@DanCech DanCech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

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.

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.