test: Add unit and integration tests for API and storage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
107
backend/internal/storage/testhelpers_test.go
Normal file
107
backend/internal/storage/testhelpers_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
//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
|
||||
}
|
||||
Reference in New Issue
Block a user