Files
music-agregator/test/component/setup_test.go
T
2026-05-11 15:54:25 +02:00

199 lines
4.9 KiB
Go

package component
import (
"context"
"net"
"os"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/test/bufconn"
pb "homelab.lan/music-agregator/gen/music_agregator/v1"
"homelab.lan/music-agregator/internal"
"homelab.lan/music-agregator/internal/database"
"homelab.lan/music-agregator/internal/metadata"
)
const bufSize = 1024 * 1024
type testMocks struct {
metadata *mockMetadataClient
torrent *mockTorrentClient
indexer *mockSearcher
magnet *mockResolver
}
type testSuite struct {
db *database.DB
grpcConn *grpc.ClientConn
client pb.MusicAgregatorServiceClient
pool *pgxpool.Pool
mocks *testMocks
}
func setupSuite(t *testing.T) *testSuite {
ctx := context.Background()
schemaPath := getSchemaPath(t)
schemaSQL, err := os.ReadFile(schemaPath)
require.NoError(t, err, "failed to read schema file")
migrationPath := getMigrationPath(t, "003_event_bus.sql")
migrationSQL, err := os.ReadFile(migrationPath)
require.NoError(t, err, "failed to read migration file")
pgContainer, err := postgres.Run(ctx,
"postgres:16-alpine",
postgres.WithDatabase("music_agregator_test"),
postgres.WithUsername("test"),
postgres.WithPassword("test"),
postgres.WithInitScripts(),
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
WithStartupTimeout(30*time.Second),
),
)
require.NoError(t, err, "failed to start postgres container")
t.Cleanup(func() {
if err := pgContainer.Terminate(ctx); err != nil {
t.Logf("failed to terminate postgres container: %v", err)
}
})
connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
require.NoError(t, err, "failed to get connection string")
pool, err := pgxpool.New(ctx, connStr)
require.NoError(t, err, "failed to create pgxpool")
t.Cleanup(func() {
pool.Close()
})
_, err = pool.Exec(ctx, string(schemaSQL))
require.NoError(t, err, "failed to apply schema")
_, err = pool.Exec(ctx, string(migrationSQL))
require.NoError(t, err, "failed to apply migration")
db := &database.DB{Pool: pool}
mocks := &testMocks{
metadata: &mockMetadataClient{},
torrent: &mockTorrentClient{},
indexer: &mockSearcher{},
magnet: &mockResolver{},
}
metadataSvc := metadata.NewMetadataService(mocks.metadata, db)
service := internal.NewMusicAgregatorServiceWithDeps(
metadataSvc,
mocks.indexer,
mocks.torrent,
mocks.magnet,
nil,
nil,
db,
)
server := internal.NewMusicAgregatorServerWithService(service)
lis := bufconn.Listen(bufSize)
grpcServer := grpc.NewServer()
server.Register(grpcServer)
go func() {
if err := grpcServer.Serve(lis); err != nil {
t.Logf("grpc server error: %v", err)
}
}()
t.Cleanup(func() {
grpcServer.GracefulStop()
})
conn, err := grpc.NewClient(
"passthrough://bufnet",
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return lis.DialContext(ctx)
}),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
require.NoError(t, err, "failed to create grpc client connection")
t.Cleanup(func() {
conn.Close()
})
client := pb.NewMusicAgregatorServiceClient(conn)
return &testSuite{
db: db,
grpcConn: conn,
client: client,
pool: pool,
mocks: mocks,
}
}
func getSchemaPath(t *testing.T) string {
_, currentFile, _, ok := runtime.Caller(0)
require.True(t, ok, "failed to get current file path")
testDir := filepath.Dir(currentFile)
schemaPath := filepath.Join(testDir, "..", "..", "..", "containers", "database", "music-agregator", "002_schema.sql")
if _, err := os.Stat(schemaPath); os.IsNotExist(err) {
schemaPath = filepath.Join(testDir, "..", "..", "containers", "database", "music-agregator", "002_schema.sql")
}
return schemaPath
}
func getMigrationPath(t *testing.T, filename string) string {
_, currentFile, _, ok := runtime.Caller(0)
require.True(t, ok, "failed to get current file path")
testDir := filepath.Dir(currentFile)
migrationPath := filepath.Join(testDir, "..", "..", "..", "containers", "database", "music-agregator", filename)
if _, err := os.Stat(migrationPath); os.IsNotExist(err) {
migrationPath = filepath.Join(testDir, "..", "..", "containers", "database", "music-agregator", filename)
}
return migrationPath
}
func cleanTables(t *testing.T, pool *pgxpool.Pool) {
ctx := context.Background()
tables := []string{
"album_events",
"workflow_runs",
"download_files",
"downloads",
"torrents",
"tracks",
"albums",
"artists",
}
for _, table := range tables {
_, err := pool.Exec(ctx, "TRUNCATE TABLE "+table+" CASCADE")
require.NoError(t, err, "failed to truncate table %s", table)
}
}