103 lines
2.9 KiB
Go
103 lines
2.9 KiB
Go
package harness
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
testcontainers "github.com/testcontainers/testcontainers-go"
|
|
rediscontainer "github.com/testcontainers/testcontainers-go/modules/redis"
|
|
)
|
|
|
|
const redisImage = "redis:7"
|
|
|
|
// RedisEnv carries the per-package Redis fixture. The container is
|
|
// started lazily on the first EnsureRedis call and torn down by
|
|
// ShutdownRedis at TestMain exit. Both stream consumers and the
|
|
// per-game lease store hit this real Redis (miniredis would suffice
|
|
// for streams alone, but the lease semantics and eviction-by-TTL we
|
|
// rely on in `health_test` are easier to verify against a real
|
|
// daemon).
|
|
type RedisEnv struct {
|
|
container *rediscontainer.RedisContainer
|
|
addr string
|
|
}
|
|
|
|
// Addr returns the externally reachable host:port of the Redis
|
|
// container. Both the runtime under test and the harness-owned client
|
|
// connect through the same endpoint.
|
|
func (env *RedisEnv) Addr() string { return env.addr }
|
|
|
|
// NewClient opens a fresh `*redis.Client` against the harness Redis.
|
|
// Tests close their client through `t.Cleanup`; the harness keeps no
|
|
// shared client to avoid cross-test connection-pool surprises.
|
|
func (env *RedisEnv) NewClient(t testing.TB) *redis.Client {
|
|
t.Helper()
|
|
client := redis.NewClient(&redis.Options{Addr: env.addr})
|
|
t.Cleanup(func() { _ = client.Close() })
|
|
return client
|
|
}
|
|
|
|
var (
|
|
redisOnce sync.Once
|
|
redisEnv *RedisEnv
|
|
redisErr error
|
|
)
|
|
|
|
// EnsureRedis starts the per-package Redis container on first
|
|
// invocation and returns it. When Docker is unavailable the helper
|
|
// calls `t.Skip` so the suite stays green on hosts without a daemon.
|
|
func EnsureRedis(t testing.TB) *RedisEnv {
|
|
t.Helper()
|
|
redisOnce.Do(func() {
|
|
redisEnv, redisErr = startRedis()
|
|
})
|
|
if redisErr != nil {
|
|
t.Skipf("rtmanager integration: redis container start failed (Docker unavailable?): %v", redisErr)
|
|
}
|
|
return redisEnv
|
|
}
|
|
|
|
// FlushRedis drops every key on the harness Redis. Tests call it from
|
|
// their setup so streams, offset records, and leases from previous
|
|
// scenarios do not leak.
|
|
func FlushRedis(t testing.TB) {
|
|
t.Helper()
|
|
env := EnsureRedis(t)
|
|
client := redis.NewClient(&redis.Options{Addr: env.addr})
|
|
defer func() { _ = client.Close() }()
|
|
if _, err := client.FlushAll(context.Background()).Result(); err != nil {
|
|
t.Fatalf("flush rtmanager redis: %v", err)
|
|
}
|
|
}
|
|
|
|
// ShutdownRedis terminates the shared container. `TestMain` invokes it
|
|
// after `m.Run`.
|
|
func ShutdownRedis() {
|
|
if redisEnv == nil {
|
|
return
|
|
}
|
|
if redisEnv.container != nil {
|
|
_ = testcontainers.TerminateContainer(redisEnv.container)
|
|
}
|
|
redisEnv = nil
|
|
}
|
|
|
|
func startRedis() (*RedisEnv, error) {
|
|
ctx := context.Background()
|
|
container, err := rediscontainer.Run(ctx, redisImage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addr, err := container.Endpoint(ctx, "")
|
|
if err != nil {
|
|
_ = testcontainers.TerminateContainer(container)
|
|
return nil, err
|
|
}
|
|
return &RedisEnv{
|
|
container: container,
|
|
addr: addr,
|
|
}, nil
|
|
}
|