chore: consume the testredis module in redis

This commit is contained in:
Manuel de la Peña
2025-04-30 12:37:58 +02:00
parent 76a7b0a657
commit 05b5be3f18
3 changed files with 33 additions and 160 deletions

View File

@@ -1,161 +1,30 @@
package redis
import (
"context"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/redis"
testredis "github.com/gofiber/storage/testhelpers/redis"
)
const (
// redisImage is the default image used for running Redis in tests.
redisImage = "docker.io/redis:7"
redisImageEnvVar = "TEST_REDIS_IMAGE"
redisPort = "6379/tcp"
)
type testStoreSettings struct {
address bool
hostPort bool
url bool
// TLS settings
secureURL bool
mtlsDisabled bool
tls bool
}
type testStoreOption func(*testStoreSettings)
// withAddress sets the test store to use address-based connection.
func withAddress() testStoreOption {
return func(o *testStoreSettings) {
o.address = true
}
}
// withHostPort sets the test store to use host and port based connection.
func withHostPort() testStoreOption {
return func(o *testStoreSettings) {
o.hostPort = true
}
}
// withTLS configures the test store to use TLS.
// Parameters:
// - secureURL: when true, uses "rediss://" scheme, otherwise uses "redis://"
// - mtlsDisabled: when true, disables mutual TLS authentication (--tls-auth-clients no)
func withTLS(secureURL bool, mtlsDisabled bool) testStoreOption {
return func(o *testStoreSettings) {
o.tls = true
o.secureURL = secureURL
o.mtlsDisabled = mtlsDisabled
}
}
// withURL sets the test store to use a URL.
// Use it when you want to explicitly combine multiple addresses in the same test
// to verify which one is being used.
// - true: the URL will receive the URI provided by the testcontainer
// - false: the URL will be set to an empty string
func withURL(useContainerURI bool) testStoreOption {
return func(o *testStoreSettings) {
o.url = useContainerURI
}
}
// newConfigFromContainer creates a Redis configuration using a test container.
// newConfigFromContainer creates a Redis configuration using Testcontainers.
// It configures the container based on the provided options and returns a Config
// that can be used to connect to the container.
// The container is cleaned up when the test completes.
func newConfigFromContainer(t testing.TB, opts ...testStoreOption) Config {
func newConfigFromContainer(t testing.TB, opts ...testredis.Option) Config {
t.Helper()
settings := &testStoreSettings{
url: true, // by default, the URL will be set to the URI provided by the testcontainer
address: false,
hostPort: false,
}
for _, o := range opts {
o(settings)
}
img := redisImage
if imgFromEnv := os.Getenv(redisImageEnvVar); imgFromEnv != "" {
img = imgFromEnv
}
redisCtr := testredis.Start(t, opts...)
cfg := Config{
Reset: true,
}
ctx := context.Background()
tcOpts := []testcontainers.ContainerCustomizer{}
if settings.tls {
tcOpts = append(tcOpts, redis.WithTLS())
// Use Redis module's TLS options, but for the MTLS case, disable the auth-clients flag
cmds := []string{
"--port", "0",
"--tls-port", "6379",
"--tls-cert-file", "/tls/server.crt",
"--tls-key-file", "/tls/server.key",
"--tls-ca-cert-file", "/tls/ca.crt",
}
cmds = append(cmds, "--tls-auth-clients", func() string {
if settings.mtlsDisabled {
return "no"
}
return "yes"
}())
// completely override the default CMD, as the Redis module is opinionated about the CMD
tcOpts = append(tcOpts, testcontainers.WithCmd(cmds...))
}
c, err := redis.Run(ctx, img, tcOpts...)
testcontainers.CleanupContainer(t, c)
require.NoError(t, err)
cfg.TLSConfig = c.TLSConfig()
uri, err := c.ConnectionString(ctx)
require.NoError(t, err)
if settings.hostPort {
host, err := c.Host(ctx)
require.NoError(t, err)
port, err := c.MappedPort(ctx, redisPort)
require.NoError(t, err)
cfg.Host = host
cfg.Port = port.Int()
}
if settings.address {
// trim the schemes from the URI
addr := strings.TrimPrefix(uri, "redis://")
addr = strings.TrimPrefix(addr, "rediss://")
cfg.Addrs = []string{addr}
}
if settings.url {
cfg.URL = uri
}
if !settings.secureURL {
// Replace the scheme with the insecure one
cfg.URL = strings.Replace(cfg.URL, "rediss://", "redis://", 1)
Reset: true,
TLSConfig: redisCtr.TLSConfig,
Host: redisCtr.Host,
Port: redisCtr.Port,
Addrs: redisCtr.Addrs,
URL: redisCtr.URL,
}
return cfg
@@ -165,7 +34,7 @@ func newConfigFromContainer(t testing.TB, opts ...testStoreOption) Config {
// It configures the container based on the provided options and returns a Storage
// instance connected to the container. The caller is responsible for calling
// Close() on the returned Storage when done.
func newTestStore(t testing.TB, opts ...testStoreOption) *Storage {
func newTestStore(t testing.TB, opts ...testredis.Option) *Storage {
return New(newConfigFromContainer(t, opts...))
}
@@ -330,7 +199,7 @@ func Test_Redis_Initialize_WithHostPort(t *testing.T) {
val = []byte("kent")
)
testStore := newTestStore(t, withHostPort())
testStore := newTestStore(t, testredis.WithHostPort())
defer testStore.Close()
err := testStore.Set(key, val, 0)
@@ -348,7 +217,7 @@ func Test_Redis_Initialize_WithURL_TLS_Verify(t *testing.T) {
testFn := func(t *testing.T, secureURL bool, mtlsDisabled bool) {
t.Helper()
testStore := newTestStore(t, withTLS(secureURL, mtlsDisabled))
testStore := newTestStore(t, testredis.WithTLS(secureURL, mtlsDisabled))
defer testStore.Close()
var (
@@ -390,7 +259,7 @@ func Test_Redis_Initialize_WithURL_TLS_Verify(t *testing.T) {
func Test_Redis_Universal_Addrs(t *testing.T) {
// This should failover and create a Single Node connection.
testStoreUniversal := newTestStore(t, withAddress())
testStoreUniversal := newTestStore(t, testredis.WithAddress())
defer testStoreUniversal.Close()
var (
key = "bruce"
@@ -416,7 +285,7 @@ func Test_Redis_Universal_With_URL_Undefined(t *testing.T) {
// This should failover to creating a regular *redis.Client
// The URL should get ignored since it's empty
// the withURL option goes last to include it in the config
testStoreUniversal := newTestStore(t, withAddress(), withURL(false))
testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithURL(false))
defer testStoreUniversal.Close()
var (
key = "bruce"
@@ -441,7 +310,7 @@ func Test_Redis_Universal_With_URL_Undefined(t *testing.T) {
func Test_Redis_Universal_With_URL_Defined(t *testing.T) {
// This should failover to creating a regular *redis.Client
// The Addrs field should get ignored since URL is defined
testStoreUniversal := newTestStore(t, withAddress(), withURL(true))
testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithURL(true))
defer testStoreUniversal.Close()
var (
@@ -467,7 +336,7 @@ func Test_Redis_Universal_With_URL_Defined(t *testing.T) {
func Test_Redis_Universal_With_HostPort(t *testing.T) {
// This should failover to creating a regular *redis.Client
// The Host and Port should get ignored since Addrs is defined
testStoreUniversal := newTestStore(t, withAddress(), withHostPort(), withURL(false))
testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithHostPort(), testredis.WithURL(false))
defer testStoreUniversal.Close()
var (
key = "bruce"
@@ -492,7 +361,7 @@ func Test_Redis_Universal_With_HostPort(t *testing.T) {
func Test_Redis_Universal_With_HostPort_And_URL(t *testing.T) {
// This should failover to creating a regular *redis.Client
// The Host and Port should get ignored since Addrs is defined
testStoreUniversal := newTestStore(t, withAddress(), withHostPort(), withURL(true))
testStoreUniversal := newTestStore(t, testredis.WithAddress(), testredis.WithHostPort(), testredis.WithURL(true))
defer testStoreUniversal.Close()
var (