Files
galaxy-game/integration/testenv/platform.go
T
2026-05-06 10:14:55 +03:00

103 lines
2.7 KiB
Go

package testenv
import (
"context"
"io"
"testing"
"github.com/testcontainers/testcontainers-go"
)
// Platform aggregates a fully booted Galaxy stack: shared Docker
// network, Postgres, Redis, mailpit, backend and gateway. Tests use
// this struct to access HTTP/gRPC endpoints, mailpit and backend
// admin without touching testcontainers directly.
type Platform struct {
Network string
Postgres *Postgres
Redis *Redis
Mailpit *Mailpit
Backend *BackendContainer
Gateway *GatewayContainer
}
// BootstrapOptions tunes platform-level knobs that flow into backend
// or gateway configuration. The zero value is valid and produces a
// stack with sensible defaults for happy-path scenarios.
type BootstrapOptions struct {
BackendExtra map[string]string
GatewayExtra map[string]string
}
// Bootstrap builds three Docker images (backend, gateway, optionally
// the engine in the caller), spins up Postgres, Redis, mailpit, then
// boots backend and gateway connected to those services. It registers
// t.Cleanup hooks for every component, so callers do not own
// teardown.
//
// The function calls RequireDocker and skips the test gracefully if
// the daemon is unreachable, so every scenario can start with a
// single Bootstrap call.
func Bootstrap(t *testing.T, opts BootstrapOptions) *Platform {
t.Helper()
RequireDocker(t)
net := StartNetwork(t)
pg := StartPostgres(t, net.Name)
redis := StartRedis(t, net.Name)
mp := StartMailpit(t, net.Name)
geoip := SyntheticGeoIPDB(t)
backend := StartBackend(t, BackendOptions{
NetworkAlias: "backend",
NetworkName: net.Name,
PostgresDSN: pg.NetworkDSN,
MailpitHost: "mailpit",
MailpitPort: 1025,
GeoIPHostPath: geoip,
Extra: opts.BackendExtra,
})
gateway := StartGateway(t, GatewayOptions{
NetworkAlias: "gateway",
NetworkName: net.Name,
BackendHTTPURL: "http://backend:8080",
BackendGRPCURL: "backend:8081",
RedisAddr: "redis:6379",
Extra: opts.GatewayExtra,
})
plat := &Platform{
Network: net.Name,
Postgres: pg,
Redis: redis,
Mailpit: mp,
Backend: backend,
Gateway: gateway,
}
t.Cleanup(func() {
if !t.Failed() {
return
}
dumpLogs(t, "backend", backend.Container)
dumpLogs(t, "gateway", gateway.Container)
})
return plat
}
// dumpLogs writes the container's stdout/stderr to test output. Used
// only on failure to surface backend / gateway diagnostics.
func dumpLogs(t *testing.T, name string, c testcontainers.Container) {
t.Helper()
if c == nil {
return
}
rc, err := c.Logs(context.Background())
if err != nil {
t.Logf("%s logs unavailable: %v", name, err)
return
}
defer rc.Close()
body, _ := io.ReadAll(rc)
t.Logf("--- %s container logs ---\n%s", name, string(body))
}