108 lines
2.5 KiB
Go
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
|
|
}
|