feat: runtime manager
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user