Skip to content

fix: stop leaking postgres testcontainers in coverage runs#2950

Merged
omer-topal merged 2 commits intomasterfrom
fix/postgres-testcontainer-cleanup
May 7, 2026
Merged

fix: stop leaking postgres testcontainers in coverage runs#2950
omer-topal merged 2 commits intomasterfrom
fix/postgres-testcontainer-cleanup

Conversation

@omer-topal
Copy link
Copy Markdown
Contributor

@omer-topal omer-topal commented May 7, 2026

Summary by CodeRabbit

  • Refactor

    • Introduced a test Postgres instance wrapper to centralize shutdown and cleanup, improving database lifecycle management and error-safe teardown.
  • Tests

    • Updated numerous Postgres integration tests to use the wrapper’s underlying DB handle consistently, streamlining setup/teardown and making tests more reliable.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors the Postgres test infrastructure by introducing a PostgresInstance wrapper struct that centralizes shutdown behavior and idempotent Close() semantics, then updates Postgres-related tests to access the underlying database handle via db.Postgres instead of explicit type assertions to *PQDatabase.Postgres.

Changes

PostgresInstance Wrapper & Test Harness Update

Layer / File(s) Summary
PostgresInstance Type
pkg/testinstance/postgres.go
New PostgresInstance struct holds *PQDatabase.Postgres, testcontainer, sync.Once, and error state; implements idempotent Close() with 30s timeout and GetEngineType()/IsReady() delegation methods.
PostgresDB Setup
pkg/testinstance/postgres.go
Return type changes from database.Database to *PostgresInstance; error handling refactored into cleanup and expectNoError helper closures that ensure partial resources are closed on failure.
Bundle & Schema Tests
internal/storage/postgres/{bundle_reader,bundle_writer,schema_reader,schema_writer}_test.go
Test db typed as *testinstance.PostgresInstance; reader/writer constructors updated to use db.Postgres; closed-DB error cases updated to close via db.Close() and pass db.Postgres.
Data Reader & Writer Tests
internal/storage/postgres/{data_reader,data_writer}_test.go
Constructors and many error-case setups updated to pass db.Postgres and to close the wrapper via db.Close() for closed-DB tests.
Tenant, GC, Watch & Utils Tests
internal/storage/postgres/{tenant_reader,tenant_writer,gc/{gc,options},watch}_test.go, internal/storage/postgres/utils/common_test.go
Tests updated to use db.Postgres for constructors and WritePool access; GC option tests assert gc.database == db.Postgres.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Permify/permify#1486: Refactors tests to use testinstance.PostgresDB; related to the test DB initialization changes.
  • Permify/permify#2652: Modifies GC tests overlapping with gc/gc_test.go updates.
  • Permify/permify#2453: Also refactors test initialization to consume pkg/testinstance.PostgresDB and its Postgres handle.

Suggested reviewers

  • tolgaOzen

Poem

🐰 A rabbit hops through test-land fair,
Replaced the casts with db.Postgres flair,
sync.Once closes tidy, no mess,
Tests point straight at the handle — progress! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: stop leaking postgres testcontainers in coverage runs' clearly and concisely summarizes the main objective of the changeset, which is to fix resource leaks in Postgres testcontainers during coverage test runs by introducing proper cleanup mechanisms.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/postgres-testcontainer-cleanup

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
Copy Markdown

@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.

Caution

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

⚠️ Outside diff range comments (5)
internal/storage/postgres/utils/common_test.go (2)

499-517: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Container leak: local db shadows outer db, escaping AfterEach cleanup.

Line 502 declares db := database as a local variable that shadows the outer db at line 380. AfterEach only closes the outer db (from BeforeEach). If any assertion at lines 506–514 fails before reaching db.Close() at line 516, the new container is never terminated.

🐛 Proposed fix
 database := testinstance.PostgresDB(version)
-db := database
-writePool := db.Postgres.WritePool
+localDB := database
+defer func() { _ = localDB.Close() }()
+writePool := localDB.Postgres.WritePool
 
 versionStr, err := utils.EnsureDBVersion(writePool)
 Expect(err).ShouldNot(HaveOccurred())
 
 versionNum, parseErr := strconv.Atoi(versionStr)
 Expect(parseErr).ShouldNot(HaveOccurred())
 
 Expect(versionNum).Should(BeNumerically(">=", 100000))
 Expect(versionNum).Should(BeNumerically("<=", 999999))
-
-err = db.Close()
-Expect(err).ShouldNot(HaveOccurred())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/utils/common_test.go` around lines 499 - 517, The
test creates a new local variable "db" that shadows the outer test fixture and
prevents AfterEach from cleaning the container; change the shadowing assignment
in the block that calls testinstance.PostgresDB(version) so it reuses the outer
db variable (or rename the local to a non-shadowing name) instead of "db :=
database", ensuring the container returned by testinstance.PostgresDB(version)
is closed by the existing AfterEach cleanup; check the block around
EnsureDBVersion, versionStr parsing and the final db.Close call to confirm the
outer db is the same instance used throughout.

454-483: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Container leak: testDB.Close() won't run if any assertion fails.

If any Expect between lines 469–477 panics (Ginkgo test failure), execution jumps to AfterEach, which only closes the outer db (from BeforeEach). testDB created inside the loop is never closed, leaking the testcontainer — directly contradicting this PR's goal.

Use defer immediately after allocation:

🐛 Proposed fix
 database := testinstance.PostgresDB(pgVersion)
 testDB := database
+defer func() { _ = testDB.Close() }()
 testPool := testDB.Postgres.WritePool
 
 version, err := utils.EnsureDBVersion(testPool)
 
 Expect(err).ShouldNot(HaveOccurred())
 Expect(version).ShouldNot(BeEmpty())
 
 versionNum, parseErr := strconv.Atoi(version)
 Expect(parseErr).ShouldNot(HaveOccurred())
 Expect(versionNum).Should(BeNumerically(">=", 130008))
 
-// Clean up
-err = testDB.Close()
-Expect(err).ShouldNot(HaveOccurred())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/utils/common_test.go` around lines 454 - 483, The
loop creates per-version test containers with testinstance.PostgresDB (assigned
to variable testDB) but calls testDB.Close() only at the end, so a failing
Expect can leak a container; fix by deferring close immediately after creating
the DB (i.e., call defer func() { _ = testDB.Close() }() or defer testDB.Close()
right after testDB := database) so the container is always cleaned up even if
assertions panic; keep the rest of the checks (EnsureDBVersion, strconv.Atoi,
Expect calls) unchanged and reference testinstance.PostgresDB, testDB, testPool,
utils.EnsureDBVersion, and testDB.Close in your change.
internal/storage/postgres/tenant_writer_test.go (1)

332-344: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inline separateDB containers can leak if an assertion fails before separateDB.Close().

This is the exact problem the PR aims to fix. When a Ginkgo Expect(...) assertion fails inside an It block, Ginkgo recovers the panic and runs AfterEach — but AfterEach only cleans up the outer db. Any separateDB created inline without a DeferCleanup is silently leaked if the test fails between container creation and separateDB.Close().

For example, in the test at line 323, if separateWriter.CreateTenant(...) fails, separateDB.Close() is never reached:

separateDB := testinstance.PostgresDB("14")
separateWriter := NewTenantWriter(separateDB.Postgres)

_, err = separateWriter.CreateTenant(...)  // ← fail here → separateDB leaks
Expect(err).ShouldNot(HaveOccurred())

err = separateDB.Close()  // ← never reached

Apply this fix to all affected It blocks:

🛡️ Proposed fix — register cleanup immediately after creation
 separateDB := testinstance.PostgresDB("14")
+DeferCleanup(separateDB.Close)
 separateWriter := NewTenantWriter(separateDB.Postgres)

Affected locations: lines 332, 359, 407, 435, 463 (and similar patterns throughout the DeleteTenant Error Handling context).

Also applies to: 359-372, 407-421, 435-449, 463-477

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/tenant_writer_test.go` around lines 332 - 344,
Multiple It blocks create an inline testinstance Postgres container (separateDB)
and use it to build a NewTenantWriter but do not register cleanup immediately,
risking leaked containers if an Expect fails; fix by calling t.DeferCleanup or
Ginkgo's DeferCleanup on the separateDB.Close right after creating separateDB
(immediately after the separateDB := testinstance.PostgresDB("...") line) in
each affected block (references: separateDB, separateWriter :=
NewTenantWriter(separateDB.Postgres), separateWriter.CreateTenant(...),
writerWithClosedDB) so the container is always closed even if the test assertion
fails.
internal/storage/postgres/gc/gc_test.go (2)

637-650: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

freshDB can leak if the freshGC.Run() assertion fails.

Same pattern as the separateDB issue in tenant_writer_test.go. If freshGC.Run() produces an unexpected error, freshDB.Close() is never called:

freshDB := testinstance.PostgresDB("14")   // ← new container
freshGC := NewGC(freshDB.Postgres, ...)

err := freshGC.Run()
Expect(err).ShouldNot(HaveOccurred())   // ← fail here → freshDB leaks

err = freshDB.Close()   // ← never reached
🛡️ Proposed fix
 freshDB := testinstance.PostgresDB("14")
+DeferCleanup(freshDB.Close)
 freshGC := NewGC(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/gc/gc_test.go` around lines 637 - 650, The test can
leak the test Postgres container because freshDB.Close() is not guaranteed to
run if Expect(err).ShouldNot(HaveOccurred()) fails; wrap the resource cleanup so
it always runs (e.g., call freshDB.Close() via defer immediately after creating
freshDB or register it with the test cleanup) so that freshDB is closed
regardless of freshGC.Run() outcome; locate the freshDB variable and the
NewGC/freshGC.Run() call in this test and add a guaranteed cleanup (defer
freshDB.Close() or equivalent test teardown) right after freshDB :=
testinstance.PostgresDB("14") to ensure Close() runs even on assertion failures.

373-385: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

closedDB container also benefits from DeferCleanup for consistency.

While closedDB.Close() is called immediately (before any further assertions), if that Close() itself fails the assertion at line 374, Ginkgo catches the panic and the container leaks without a registered cleanup:

🛡️ Proposed fix
 closedDB := testinstance.PostgresDB("14")
+DeferCleanup(closedDB.Close)
 err := closedDB.Close()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/gc/gc_test.go` around lines 373 - 385, The test
creates closedDB via testinstance.PostgresDB("14") then calls closedDB.Close()
directly which can panic and leak the container; register a cleanup immediately
after creating closedDB (e.g., t.DeferCleanup or Ginkgo's DeferCleanup) using
closedDB.Close (or a wrapper) before calling closedDB.Close() and then perform
the Close/assert; this ensures the container is always cleaned up even if the
explicit Close() assertion in the test for closedDB fails; references: closedDB,
PostgresDB("14"), NewGC, Run.
🧹 Nitpick comments (3)
internal/storage/postgres/watch_test.go (1)

114-336: ⚖️ Poor tradeoff

Duplicate It bodies throughout the error-handling contexts.

All the It blocks in getRecentXIDs Error Handling (lines 186-228) and getChanges Error Handling (lines 231-335) have identical test bodies — same setup, same assertion, testing the same error path. They don't exercise distinct code paths (all call db.Close() first, which causes every subsequent operation to fail with the same error). Consider collapsing them into parameterized table tests or removing duplicates to reduce maintenance burden.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/watch_test.go` around lines 114 - 336, The tests
duplicate identical bodies for many It blocks causing maintenance overhead;
replace the repeated It cases in getRecentXIDs and getChanges Error Handling
with a single parameterized/table-driven test (e.g., Ginkgo DescribeTable or a
for-loop of cases) that enumerates the different case names but reuses the
shared setup (close db, NewWatcher(closedDB)) and asserts the same error
substring; target the repeated call sites getRecentXIDs(ctx, 0, "t1") and
getChanges(ctx, PQDatabase.XID8{Uint: 1}, "t1") and consolidate their
expectations into table entries so each logical case is represented without
duplicating the test body.
pkg/testinstance/postgres.go (1)

21-56: 💤 Low value

LGTM — PostgresInstance wrapper is well-structured.

The compile-time interface assertion at line 28, sync.Once-backed idempotent Close(), and nil-guarded sub-closures are all correct. One optional hardening: GetEngineType() and IsReady() will panic if called on a zero-value &PostgresInstance{} (nil Postgres). Since only PostgresDB() constructs these and always populates Postgres, this is safe in practice, but a nil guard would be defensive.

🛡️ Optional nil guard
 func (p *PostgresInstance) GetEngineType() string {
+	if p.Postgres == nil {
+		return ""
+	}
 	return p.Postgres.GetEngineType()
 }

 func (p *PostgresInstance) IsReady(ctx context.Context) (bool, error) {
+	if p.Postgres == nil {
+		return false, nil
+	}
 	return p.Postgres.IsReady(ctx)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/testinstance/postgres.go` around lines 21 - 56, The GetEngineType and
IsReady methods on PostgresInstance can panic on a zero-value instance because
p.Postgres may be nil; add defensive nil checks in the methods
(PostgresInstance.GetEngineType and PostgresInstance.IsReady) to return a safe
default (e.g., empty string for GetEngineType) or a clear error for IsReady when
p.Postgres is nil, so callers never dereference a nil Postgres pointer; keep
behavior unchanged when p.Postgres is present.
internal/storage/postgres/gc/options_test.go (1)

15-18: ⚡ Quick win

Consider sharing a single container across the entire options-test suite.

Every It block in this file spins up and tears down a full Postgres testcontainer, yet none of them execute any database queries — they only test GC option-setter functions. Moving the container lifecycle to BeforeSuite/AfterSuite (or SynchronizedBeforeSuite/SynchronizedAfterSuite) would cut the container startup overhead from O(number of tests) to O(1) for this suite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/gc/options_test.go` around lines 15 - 18, The tests
currently create a fresh Postgres container in BeforeEach via
testinstance.PostgresDB (see the BeforeEach block) which is wasteful; move the
container lifecycle to a suite-level setup/teardown by creating the DB once in
BeforeSuite (or SynchronizedBeforeSuite for parallel Ginkgo nodes) and
terminating it in AfterSuite (or SynchronizedAfterSuite), make the db variable
package-level so individual It blocks reuse it, and remove the per-test
Terminate/creation in BeforeEach/AfterEach to eliminate repeated container
startups.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/storage/postgres/gc/gc_test.go`:
- Around line 637-650: The test can leak the test Postgres container because
freshDB.Close() is not guaranteed to run if
Expect(err).ShouldNot(HaveOccurred()) fails; wrap the resource cleanup so it
always runs (e.g., call freshDB.Close() via defer immediately after creating
freshDB or register it with the test cleanup) so that freshDB is closed
regardless of freshGC.Run() outcome; locate the freshDB variable and the
NewGC/freshGC.Run() call in this test and add a guaranteed cleanup (defer
freshDB.Close() or equivalent test teardown) right after freshDB :=
testinstance.PostgresDB("14") to ensure Close() runs even on assertion failures.
- Around line 373-385: The test creates closedDB via
testinstance.PostgresDB("14") then calls closedDB.Close() directly which can
panic and leak the container; register a cleanup immediately after creating
closedDB (e.g., t.DeferCleanup or Ginkgo's DeferCleanup) using closedDB.Close
(or a wrapper) before calling closedDB.Close() and then perform the
Close/assert; this ensures the container is always cleaned up even if the
explicit Close() assertion in the test for closedDB fails; references: closedDB,
PostgresDB("14"), NewGC, Run.

In `@internal/storage/postgres/tenant_writer_test.go`:
- Around line 332-344: Multiple It blocks create an inline testinstance Postgres
container (separateDB) and use it to build a NewTenantWriter but do not register
cleanup immediately, risking leaked containers if an Expect fails; fix by
calling t.DeferCleanup or Ginkgo's DeferCleanup on the separateDB.Close right
after creating separateDB (immediately after the separateDB :=
testinstance.PostgresDB("...") line) in each affected block (references:
separateDB, separateWriter := NewTenantWriter(separateDB.Postgres),
separateWriter.CreateTenant(...), writerWithClosedDB) so the container is always
closed even if the test assertion fails.

In `@internal/storage/postgres/utils/common_test.go`:
- Around line 499-517: The test creates a new local variable "db" that shadows
the outer test fixture and prevents AfterEach from cleaning the container;
change the shadowing assignment in the block that calls
testinstance.PostgresDB(version) so it reuses the outer db variable (or rename
the local to a non-shadowing name) instead of "db := database", ensuring the
container returned by testinstance.PostgresDB(version) is closed by the existing
AfterEach cleanup; check the block around EnsureDBVersion, versionStr parsing
and the final db.Close call to confirm the outer db is the same instance used
throughout.
- Around line 454-483: The loop creates per-version test containers with
testinstance.PostgresDB (assigned to variable testDB) but calls testDB.Close()
only at the end, so a failing Expect can leak a container; fix by deferring
close immediately after creating the DB (i.e., call defer func() { _ =
testDB.Close() }() or defer testDB.Close() right after testDB := database) so
the container is always cleaned up even if assertions panic; keep the rest of
the checks (EnsureDBVersion, strconv.Atoi, Expect calls) unchanged and reference
testinstance.PostgresDB, testDB, testPool, utils.EnsureDBVersion, and
testDB.Close in your change.

---

Nitpick comments:
In `@internal/storage/postgres/gc/options_test.go`:
- Around line 15-18: The tests currently create a fresh Postgres container in
BeforeEach via testinstance.PostgresDB (see the BeforeEach block) which is
wasteful; move the container lifecycle to a suite-level setup/teardown by
creating the DB once in BeforeSuite (or SynchronizedBeforeSuite for parallel
Ginkgo nodes) and terminating it in AfterSuite (or SynchronizedAfterSuite), make
the db variable package-level so individual It blocks reuse it, and remove the
per-test Terminate/creation in BeforeEach/AfterEach to eliminate repeated
container startups.

In `@internal/storage/postgres/watch_test.go`:
- Around line 114-336: The tests duplicate identical bodies for many It blocks
causing maintenance overhead; replace the repeated It cases in getRecentXIDs and
getChanges Error Handling with a single parameterized/table-driven test (e.g.,
Ginkgo DescribeTable or a for-loop of cases) that enumerates the different case
names but reuses the shared setup (close db, NewWatcher(closedDB)) and asserts
the same error substring; target the repeated call sites getRecentXIDs(ctx, 0,
"t1") and getChanges(ctx, PQDatabase.XID8{Uint: 1}, "t1") and consolidate their
expectations into table entries so each logical case is represented without
duplicating the test body.

In `@pkg/testinstance/postgres.go`:
- Around line 21-56: The GetEngineType and IsReady methods on PostgresInstance
can panic on a zero-value instance because p.Postgres may be nil; add defensive
nil checks in the methods (PostgresInstance.GetEngineType and
PostgresInstance.IsReady) to return a safe default (e.g., empty string for
GetEngineType) or a clear error for IsReady when p.Postgres is nil, so callers
never dereference a nil Postgres pointer; keep behavior unchanged when
p.Postgres is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9004d878-1c47-4320-a547-37a257701da9

📥 Commits

Reviewing files that changed from the base of the PR and between 2734499 and e086571.

📒 Files selected for processing (13)
  • internal/storage/postgres/bundle_reader_test.go
  • internal/storage/postgres/bundle_writer_test.go
  • internal/storage/postgres/data_reader_test.go
  • internal/storage/postgres/data_writer_test.go
  • internal/storage/postgres/gc/gc_test.go
  • internal/storage/postgres/gc/options_test.go
  • internal/storage/postgres/schema_reader_test.go
  • internal/storage/postgres/schema_writer_test.go
  • internal/storage/postgres/tenant_reader_test.go
  • internal/storage/postgres/tenant_writer_test.go
  • internal/storage/postgres/utils/common_test.go
  • internal/storage/postgres/watch_test.go
  • pkg/testinstance/postgres.go

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.60%. Comparing base (2734499) to head (b0bd540).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2950      +/-   ##
==========================================
+ Coverage   82.57%   82.60%   +0.03%     
==========================================
  Files          74       74              
  Lines        8300     8300              
==========================================
+ Hits         6853     6855       +2     
+ Misses        911      909       -2     
  Partials      536      536              

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/storage/postgres/tenant_writer_test.go`:
- Around line 288-289: The test currently swallows errors by calling
DeferCleanup(func() { _ = separateDB.Close() }) and then immediately calling err
:= separateDB.Close(); instead, change the cleanup to check and report the
error: use DeferCleanup(func() { if err := separateDB.Close(); err != nil {
t.Errorf("closing separateDB: %v", err) } }) and remove the redundant immediate
err := separateDB.Close() call so cleanup failures for separateDB are surfaced;
reference the separateDB variable and the DeferCleanup call when making this
change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e7cadb5-0120-476c-9a5e-e858b5794c03

📥 Commits

Reviewing files that changed from the base of the PR and between e086571 and b0bd540.

📒 Files selected for processing (3)
  • internal/storage/postgres/gc/gc_test.go
  • internal/storage/postgres/tenant_writer_test.go
  • internal/storage/postgres/utils/common_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/storage/postgres/utils/common_test.go
  • internal/storage/postgres/gc/gc_test.go

Comment on lines +288 to 289
DeferCleanup(func() { _ = separateDB.Close() })
err := separateDB.Close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t swallow cleanup errors in DeferCleanup.

On Line 288, ignoring separateDB.Close() errors (_ = ...) can hide cleanup failures and make container-leak regressions harder to detect when the test exits early.

Suggested fix
- DeferCleanup(func() { _ = separateDB.Close() })
+ DeferCleanup(func() {
+   Expect(separateDB.Close()).ShouldNot(HaveOccurred())
+ })
📝 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
DeferCleanup(func() { _ = separateDB.Close() })
err := separateDB.Close()
DeferCleanup(func() {
Expect(separateDB.Close()).ShouldNot(HaveOccurred())
})
err := separateDB.Close()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/storage/postgres/tenant_writer_test.go` around lines 288 - 289, The
test currently swallows errors by calling DeferCleanup(func() { _ =
separateDB.Close() }) and then immediately calling err := separateDB.Close();
instead, change the cleanup to check and report the error: use
DeferCleanup(func() { if err := separateDB.Close(); err != nil {
t.Errorf("closing separateDB: %v", err) } }) and remove the redundant immediate
err := separateDB.Close() call so cleanup failures for separateDB are surfaced;
reference the separateDB variable and the DeferCleanup call when making this
change.

@omer-topal omer-topal merged commit 78e2cfb into master May 7, 2026
15 checks passed
@omer-topal omer-topal deleted the fix/postgres-testcontainer-cleanup branch May 7, 2026 12:19
@github-actions github-actions Bot locked and limited conversation to collaborators May 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant