gitlab-org--gitlab-foss/workhorse/internal/redis/redis_test.go

229 lines
5.1 KiB
Go

package redis
import (
"net"
"testing"
"time"
"github.com/gomodule/redigo/redis"
"github.com/rafaeljusto/redigomock/v3"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
)
func mockRedisServer(t *testing.T, connectReceived *bool) string {
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.Nil(t, err)
go func() {
defer ln.Close()
conn, err := ln.Accept()
require.Nil(t, err)
*connectReceived = true
conn.Write([]byte("OK\n"))
}()
return ln.Addr().String()
}
// Setup a MockPool for Redis
//
// Returns a teardown-function and the mock-connection
func setupMockPool() (*redigomock.Conn, func()) {
conn := redigomock.NewConn()
cfg := &config.RedisConfig{URL: config.TomlURL{}}
Configure(cfg, func(_ *config.RedisConfig, _ bool) func() (redis.Conn, error) {
return func() (redis.Conn, error) {
return conn, nil
}
})
return conn, func() {
pool = nil
}
}
func TestDefaultDialFunc(t *testing.T) {
testCases := []struct {
scheme string
}{
{
scheme: "tcp",
},
{
scheme: "redis",
},
}
for _, tc := range testCases {
t.Run(tc.scheme, func(t *testing.T) {
connectReceived := false
a := mockRedisServer(t, &connectReceived)
parsedURL := helper.URLMustParse(tc.scheme + "://" + a)
cfg := &config.RedisConfig{URL: config.TomlURL{URL: *parsedURL}}
dialer := DefaultDialFunc(cfg, true)
conn, err := dialer()
require.Nil(t, err)
conn.Receive()
require.True(t, connectReceived)
})
}
}
func TestConfigureNoConfig(t *testing.T) {
pool = nil
Configure(nil, nil)
require.Nil(t, pool, "Pool should be nil")
}
func TestConfigureMinimalConfig(t *testing.T) {
cfg := &config.RedisConfig{URL: config.TomlURL{}, Password: ""}
Configure(cfg, DefaultDialFunc)
require.NotNil(t, pool, "Pool should not be nil")
require.Equal(t, 1, pool.MaxIdle)
require.Equal(t, 1, pool.MaxActive)
require.Equal(t, 3*time.Minute, pool.IdleTimeout)
pool = nil
}
func TestConfigureFullConfig(t *testing.T) {
i, a := 4, 10
cfg := &config.RedisConfig{
URL: config.TomlURL{},
Password: "",
MaxIdle: &i,
MaxActive: &a,
}
Configure(cfg, DefaultDialFunc)
require.NotNil(t, pool, "Pool should not be nil")
require.Equal(t, i, pool.MaxIdle)
require.Equal(t, a, pool.MaxActive)
require.Equal(t, 3*time.Minute, pool.IdleTimeout)
pool = nil
}
func TestGetConnFail(t *testing.T) {
conn := Get()
require.Nil(t, conn, "Expected `conn` to be nil")
}
func TestGetConnPass(t *testing.T) {
_, teardown := setupMockPool()
defer teardown()
conn := Get()
require.NotNil(t, conn, "Expected `conn` to be non-nil")
}
func TestGetStringPass(t *testing.T) {
conn, teardown := setupMockPool()
defer teardown()
conn.Command("GET", "foobar").Expect("baz")
str, err := GetString("foobar")
require.NoError(t, err, "Expected `err` to be nil")
var value string
require.IsType(t, value, str, "Expected value to be a string")
require.Equal(t, "baz", str, "Expected it to be equal")
}
func TestGetStringFail(t *testing.T) {
_, err := GetString("foobar")
require.Error(t, err, "Expected error when not connected to redis")
}
func TestSentinelConnNoSentinel(t *testing.T) {
s := sentinelConn("", []config.TomlURL{})
require.Nil(t, s, "Sentinel without urls should return nil")
}
func TestSentinelConnDialURL(t *testing.T) {
testCases := []struct {
scheme string
}{
{
scheme: "tcp",
},
{
scheme: "redis",
},
}
for _, tc := range testCases {
t.Run(tc.scheme, func(t *testing.T) {
connectReceived := false
a := mockRedisServer(t, &connectReceived)
addrs := []string{tc.scheme + "://" + a}
var sentinelUrls []config.TomlURL
for _, a := range addrs {
parsedURL := helper.URLMustParse(a)
sentinelUrls = append(sentinelUrls, config.TomlURL{URL: *parsedURL})
}
s := sentinelConn("foobar", sentinelUrls)
require.Equal(t, len(addrs), len(s.Addrs))
for i := range addrs {
require.Equal(t, addrs[i], s.Addrs[i])
}
conn, err := s.Dial(s.Addrs[0])
require.Nil(t, err)
conn.Receive()
require.True(t, connectReceived)
})
}
}
func TestSentinelConnTwoURLs(t *testing.T) {
addrs := []string{"tcp://10.0.0.1:12345", "tcp://10.0.0.2:12345"}
var sentinelUrls []config.TomlURL
for _, a := range addrs {
parsedURL := helper.URLMustParse(a)
sentinelUrls = append(sentinelUrls, config.TomlURL{URL: *parsedURL})
}
s := sentinelConn("foobar", sentinelUrls)
require.Equal(t, len(addrs), len(s.Addrs))
for i := range addrs {
require.Equal(t, addrs[i], s.Addrs[i])
}
}
func TestDialOptionsBuildersPassword(t *testing.T) {
dopts := dialOptionsBuilder(&config.RedisConfig{Password: "foo"}, false)
require.Equal(t, 1, len(dopts))
}
func TestDialOptionsBuildersSetTimeouts(t *testing.T) {
dopts := dialOptionsBuilder(nil, true)
require.Equal(t, 2, len(dopts))
}
func TestDialOptionsBuildersSetTimeoutsConfig(t *testing.T) {
dopts := dialOptionsBuilder(nil, true)
require.Equal(t, 2, len(dopts))
}
func TestDialOptionsBuildersSelectDB(t *testing.T) {
db := 3
dopts := dialOptionsBuilder(&config.RedisConfig{DB: &db}, false)
require.Equal(t, 1, len(dopts))
}