Files
training-tracker/backend/internal/storage/testhelpers_test.go
2026-01-19 19:50:21 +01:00

108 lines
2.5 KiB
Go

//go:build integration
package storage
import (
"context"
"fmt"
"testing"
"time"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
)
// testDB holds the shared database connection for integration tests
type testDB struct {
*DB
container *postgres.PostgresContainer
}
// setupTestDB creates a PostgreSQL container and returns a connected DB instance
func setupTestDB(t *testing.T) *testDB {
t.Helper()
ctx := context.Background()
container, err := postgres.Run(ctx,
"postgres:16-alpine",
postgres.WithDatabase("training_tracker_test"),
postgres.WithUsername("test"),
postgres.WithPassword("test"),
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
WithStartupTimeout(30*time.Second),
),
)
if err != nil {
t.Fatalf("failed to start postgres container: %v", err)
}
connStr, err := container.ConnectionString(ctx, "sslmode=disable")
if err != nil {
t.Fatalf("failed to get connection string: %v", err)
}
db, err := NewDBWithConnString(ctx, connStr)
if err != nil {
t.Fatalf("failed to connect to database: %v", err)
}
if err := db.Migrate(ctx); err != nil {
t.Fatalf("failed to run migrations: %v", err)
}
return &testDB{
DB: db,
container: container,
}
}
// cleanup terminates the container and closes the database connection
func (tdb *testDB) cleanup(t *testing.T) {
t.Helper()
ctx := context.Background()
if tdb.DB != nil {
tdb.DB.Close()
}
if tdb.container != nil {
if err := tdb.container.Terminate(ctx); err != nil {
t.Logf("failed to terminate container: %v", err)
}
}
}
// truncateTables clears all data from tables for test isolation
func (tdb *testDB) truncateTables(t *testing.T) {
t.Helper()
ctx := context.Background()
tables := []string{"session_entries", "sessions", "plan_exercises", "training_plans", "exercises"}
for _, table := range tables {
_, err := tdb.ExecContext(ctx, fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table))
if err != nil {
t.Fatalf("failed to truncate %s: %v", table, err)
}
}
}
// NewDBWithConnString creates a DB instance with a custom connection string
func NewDBWithConnString(ctx context.Context, connStr string) (*DB, error) {
db, err := openDB(connStr)
if err != nil {
return nil, err
}
if err := db.PingContext(ctx); err != nil {
return nil, fmt.Errorf("failed to ping database: %w", err)
}
return &DB{db}, nil
}