fix(testutil): use shared-cache SQLite so concurrent reads see same DB

SetupTestDB used `sqlite.Open(":memory:")`, which creates a *separate*
in-memory database for every connection in GORM's pool. Sequential tests
never noticed because the pool keeps reusing one connection — but the
moment any code path issued concurrent reads (e.g. errgroup-driven
parallel COUNT queries), a goroutine could pull a fresh connection, see
no migrated tables, and explode with "no such table".

Switched to `file:testdb_<n>?mode=memory&cache=shared&_journal=memory`
with a per-test atomic counter so every connection in the pool sees the
same in-memory DB and tests stay isolated from each other through the
unique cache namespace. As a bonus, this also resolves the pre-existing
TestTaskHandler_QuickComplete flake — same root cause, just intermittent
because the pool occasionally handed out a second connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-05-01 11:00:03 -07:00
parent ce4d49caef
commit 0798ae8d74
+15 -2
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -21,7 +22,10 @@ import (
"github.com/treytartt/honeydue-api/internal/validator" "github.com/treytartt/honeydue-api/internal/validator"
) )
var i18nOnce sync.Once var (
i18nOnce sync.Once
testDBCounter uint64
)
// SetupTestDB creates an in-memory SQLite database for testing // SetupTestDB creates an in-memory SQLite database for testing
func SetupTestDB(t *testing.T) *gorm.DB { func SetupTestDB(t *testing.T) *gorm.DB {
@@ -30,7 +34,16 @@ func SetupTestDB(t *testing.T) *gorm.DB {
i18n.Init() i18n.Init()
}) })
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ // SQLite in-memory + GORM connection pool needs `cache=shared` so all
// pool connections see the same DB. With plain ":memory:", each new
// connection gets its own empty database, which makes errgroup-driven
// parallel reads explode with "no such table" once a goroutine pulls a
// fresh connection. The DSN is uniqued per test invocation (atomic
// counter) so tests don't bleed state into each other through the shared
// cache namespace.
id := atomic.AddUint64(&testDBCounter, 1)
dsn := fmt.Sprintf("file:testdb_%d?mode=memory&cache=shared&_journal=memory", id)
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), Logger: logger.Default.LogMode(logger.Silent),
}) })
require.NoError(t, err) require.NoError(t, err)