package testenv import ( "context" "testing" "time" "github.com/testcontainers/testcontainers-go" tcnetwork "github.com/testcontainers/testcontainers-go/network" "github.com/testcontainers/testcontainers-go/wait" ) // Redis holds a running Redis testcontainer reachable from the host // via HostAddr and from within the shared Docker network at the alias // "redis". Password is the requirepass value the test container was // started with so callers can pass it to gateway via env. type Redis struct { container testcontainers.Container HostAddr string Password string } // RedisIntegrationPassword is the fixed requirepass value used by all // integration scenarios. Surface it as a constant so test envs can // agree on it without per-instance plumbing. const RedisIntegrationPassword = "integration-redis-pw" // StartRedis starts a redis:7-alpine container attached to network. // The gateway uses Redis for anti-replay reservations only. func StartRedis(t *testing.T, network string) *Redis { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() req := testcontainers.ContainerRequest{ Image: "redis:7-alpine", ExposedPorts: []string{"6379/tcp"}, Cmd: []string{"redis-server", "--requirepass", RedisIntegrationPassword}, WaitingFor: wait.ForLog("Ready to accept connections"), } gcr := &testcontainers.GenericContainerRequest{ContainerRequest: req} if network != "" { _ = tcnetwork.WithNetwork([]string{"redis"}, &testcontainers.DockerNetwork{Name: network}).Customize(gcr) } gcr.Started = true container, err := testcontainers.GenericContainer(ctx, *gcr) if err != nil { t.Skipf("redis testcontainer unavailable: %v", err) } t.Cleanup(func() { if err := testcontainers.TerminateContainer(container); err != nil { t.Logf("terminate redis: %v", err) } }) host, err := container.Host(ctx) if err != nil { t.Fatalf("redis host: %v", err) } mapped, err := container.MappedPort(ctx, "6379/tcp") if err != nil { t.Fatalf("redis port: %v", err) } return &Redis{ container: container, HostAddr: HostPort(host, int(mapped.Num())), Password: RedisIntegrationPassword, } }