17fc813823
M1, M2 and M3 complete * M1, M2 and M3 complete * review changes * docs * docs
138 lines
4.1 KiB
Go
138 lines
4.1 KiB
Go
package test
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"queryorchestration/internal/client"
|
|
"queryorchestration/internal/serviceconfig"
|
|
|
|
db "queryorchestration/internal/database"
|
|
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
)
|
|
|
|
type CreateDatabaseConfig struct {
|
|
NoMigrations bool
|
|
}
|
|
|
|
const (
|
|
dbAlias = "postgres"
|
|
dbPort = 5432
|
|
)
|
|
|
|
func CreateDB(t testing.TB, cfg serviceconfig.ConfigProvider) {
|
|
CreateDBWithParams(t, cfg, &CreateDatabaseConfig{})
|
|
}
|
|
|
|
func CreateDBWithParams(t testing.TB, cfg serviceconfig.ConfigProvider, dcfg *CreateDatabaseConfig) {
|
|
port, err := nat.NewPort("tcp", strconv.Itoa(dbPort))
|
|
require.NoError(t, err)
|
|
|
|
cfg.SetDBUser("pass")
|
|
cfg.SetDBSecret("postgres")
|
|
cfg.SetDBName(GetAlias(t, dbAlias))
|
|
cfg.SetDBNoSSL(true)
|
|
|
|
network := GetNetwork(t)
|
|
|
|
req := testcontainers.ContainerRequest{
|
|
Image: "postgres:17.2-alpine3.21",
|
|
Name: "postgres_test_queryorchestration",
|
|
Env: map[string]string{
|
|
"POSTGRES_USER": cfg.GetDBUser(),
|
|
"POSTGRES_PASSWORD": cfg.GetDBSecret(),
|
|
},
|
|
Cmd: []string{"postgres", "-c", "max_connections=1000"},
|
|
ExposedPorts: []string{port.Port()},
|
|
WaitingFor: wait.ForAll(
|
|
wait.ForExposedPort(),
|
|
wait.ForListeningPort(port),
|
|
wait.ForLog("database system is ready to accept connections"),
|
|
wait.ForSQL(port, "postgres", func(host string, port nat.Port) string {
|
|
return fmt.Sprintf("postgres://%s:%s@%s:%d/postgres?sslmode=disable",
|
|
cfg.GetDBUser(), cfg.GetDBSecret(), host, port.Int())
|
|
}),
|
|
),
|
|
Networks: []string{network},
|
|
NetworkAliases: map[string][]string{
|
|
network: {dbAlias},
|
|
},
|
|
}
|
|
|
|
container, err := testcontainers.GenericContainer(t.Context(), testcontainers.GenericContainerRequest{
|
|
ContainerRequest: req,
|
|
Started: true,
|
|
Reuse: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
host, err := container.Host(t.Context())
|
|
require.NoError(t, err)
|
|
mappedPort, err := container.MappedPort(t.Context(), port)
|
|
require.NoError(t, err)
|
|
|
|
cfg.SetDBHost(host)
|
|
cfg.SetDBPort(mappedPort.Int())
|
|
|
|
if !dcfg.NoMigrations {
|
|
err := db.RunMigrations(t.Context(), cfg)
|
|
require.NoError(t, err)
|
|
|
|
err = cfg.SetDBPool(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Truncate all transactional tables so each test run starts with a clean slate.
|
|
// The postgres container is reused across runs; without this, seed data from a
|
|
// previous run causes duplicate key violations.
|
|
// Excluded:
|
|
// schema_migrations — golang-migrate tracking table; truncating would force
|
|
// migrations to re-run and break the NoChange guard.
|
|
// labels — reference/lookup table seeded by migration 110; its rows
|
|
// are never created/deleted by the application and must be
|
|
// present for the documentlabels FK to resolve.
|
|
_, err = cfg.GetDBPool().Exec(t.Context(), `
|
|
DO $$ DECLARE
|
|
r RECORD;
|
|
BEGIN
|
|
FOR r IN (
|
|
SELECT tablename
|
|
FROM pg_tables
|
|
WHERE schemaname = 'public'
|
|
AND tablename NOT IN ('schema_migrations', 'labels')
|
|
) LOOP
|
|
EXECUTE 'TRUNCATE TABLE public.' || quote_ident(r.tablename) || ' RESTART IDENTITY CASCADE';
|
|
END LOOP;
|
|
END $$;
|
|
`)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
|
|
// MustParseDate parses a date string in YYYY-MM-DD format and panics on error.
|
|
// This is useful for test data where dates are known to be valid.
|
|
func MustParseDate(dateStr string) time.Time {
|
|
t, err := time.Parse("2006-01-02", dateStr)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to parse date %q: %v", dateStr, err))
|
|
}
|
|
return t
|
|
}
|
|
|
|
// CreateTestClient creates a client using the client service, which automatically creates the root folder.
|
|
// This is the preferred way to create clients in tests to ensure proper initialization.
|
|
func CreateTestClient(t testing.TB, cfg serviceconfig.ConfigProvider, clientID, clientName string) {
|
|
t.Helper()
|
|
clientSvc := client.New(cfg)
|
|
_, err := clientSvc.Create(t.Context(), client.CreateParams{
|
|
ID: clientID,
|
|
Name: clientName,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|