mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:47:33 +00:00
feat(cache): redis cache (#8822)
# Which Problems Are Solved Add a cache implementation using Redis single mode. This does not add support for Redis Cluster or sentinel. # How the Problems Are Solved Added the `internal/cache/redis` package. All operations occur atomically, including setting of secondary indexes, using LUA scripts where needed. The [`miniredis`](https://github.com/alicebob/miniredis) package is used to run unit tests. # Additional Changes - Move connector code to `internal/cache/connector/...` and remove duplicate code from `query` and `command` packages. - Fix a missed invalidation on the restrictions projection # Additional Context Closes #8130
This commit is contained in:
714
internal/cache/connector/redis/redis_test.go
vendored
Normal file
714
internal/cache/connector/redis/redis_test.go
vendored
Normal file
@@ -0,0 +1,714 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/cache"
|
||||
)
|
||||
|
||||
type testIndex int
|
||||
|
||||
const (
|
||||
testIndexID testIndex = iota
|
||||
testIndexName
|
||||
)
|
||||
|
||||
const (
|
||||
testDB = 99
|
||||
)
|
||||
|
||||
var testIndices = []testIndex{
|
||||
testIndexID,
|
||||
testIndexName,
|
||||
}
|
||||
|
||||
type testObject struct {
|
||||
ID string
|
||||
Name []string
|
||||
}
|
||||
|
||||
func (o *testObject) Keys(index testIndex) []string {
|
||||
switch index {
|
||||
case testIndexID:
|
||||
return []string{o.ID}
|
||||
case testIndexName:
|
||||
return o.Name
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redisCache_set(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
value *testObject
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
config cache.Config
|
||||
args args
|
||||
assertions func(t *testing.T, s *miniredis.Miniredis, objectID string)
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
config: cache.Config{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
value: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, s *miniredis.Miniredis, objectID string) {
|
||||
s.CheckGet(t, "0:one", objectID)
|
||||
s.CheckGet(t, "1:foo", objectID)
|
||||
s.CheckGet(t, "1:bar", objectID)
|
||||
assert.Empty(t, s.HGet(objectID, "expiry"))
|
||||
assert.JSONEq(t, `{"ID":"one","Name":["foo","bar"]}`, s.HGet(objectID, "object"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with last use TTL",
|
||||
config: cache.Config{
|
||||
LastUseAge: time.Second,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
value: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, s *miniredis.Miniredis, objectID string) {
|
||||
s.CheckGet(t, "0:one", objectID)
|
||||
s.CheckGet(t, "1:foo", objectID)
|
||||
s.CheckGet(t, "1:bar", objectID)
|
||||
assert.Empty(t, s.HGet(objectID, "expiry"))
|
||||
assert.JSONEq(t, `{"ID":"one","Name":["foo","bar"]}`, s.HGet(objectID, "object"))
|
||||
assert.Positive(t, s.TTL(objectID))
|
||||
|
||||
s.FastForward(2 * time.Second)
|
||||
v, err := s.Get(objectID)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, v)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with last use TTL and max age",
|
||||
config: cache.Config{
|
||||
MaxAge: time.Minute,
|
||||
LastUseAge: time.Second,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
value: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, s *miniredis.Miniredis, objectID string) {
|
||||
s.CheckGet(t, "0:one", objectID)
|
||||
s.CheckGet(t, "1:foo", objectID)
|
||||
s.CheckGet(t, "1:bar", objectID)
|
||||
assert.NotEmpty(t, s.HGet(objectID, "expiry"))
|
||||
assert.JSONEq(t, `{"ID":"one","Name":["foo","bar"]}`, s.HGet(objectID, "object"))
|
||||
assert.Positive(t, s.TTL(objectID))
|
||||
|
||||
s.FastForward(2 * time.Second)
|
||||
v, err := s.Get(objectID)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, v)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with max age TTL",
|
||||
config: cache.Config{
|
||||
MaxAge: time.Minute,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
value: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, s *miniredis.Miniredis, objectID string) {
|
||||
s.CheckGet(t, "0:one", objectID)
|
||||
s.CheckGet(t, "1:foo", objectID)
|
||||
s.CheckGet(t, "1:bar", objectID)
|
||||
assert.Empty(t, s.HGet(objectID, "expiry"))
|
||||
assert.JSONEq(t, `{"ID":"one","Name":["foo","bar"]}`, s.HGet(objectID, "object"))
|
||||
assert.Positive(t, s.TTL(objectID))
|
||||
|
||||
s.FastForward(2 * time.Minute)
|
||||
v, err := s.Get(objectID)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, v)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, server := prepareCache(t, tt.config)
|
||||
rc := c.(*redisCache[testIndex, string, *testObject])
|
||||
objectID, err := rc.set(tt.args.ctx, tt.args.value)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
t.Log(rc.connector.HGetAll(context.Background(), objectID))
|
||||
tt.assertions(t, server, objectID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redisCache_Get(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
index testIndex
|
||||
key string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
config cache.Config
|
||||
preparation func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis)
|
||||
args args
|
||||
want *testObject
|
||||
wantOK bool
|
||||
}{
|
||||
{
|
||||
name: "connection error",
|
||||
config: cache.Config{},
|
||||
preparation: func(_ *testing.T, _ cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
s.RequireAuth("foobar")
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: "foo",
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "get by ID",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: "one",
|
||||
},
|
||||
want: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "get by name",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: "foo",
|
||||
},
|
||||
want: &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
},
|
||||
wantOK: true,
|
||||
},
|
||||
{
|
||||
name: "usage timeout",
|
||||
config: cache.Config{
|
||||
LastUseAge: time.Minute,
|
||||
},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
_, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
require.True(t, ok)
|
||||
s.FastForward(2 * time.Minute)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: "foo",
|
||||
},
|
||||
want: nil,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "max age timeout",
|
||||
config: cache.Config{
|
||||
MaxAge: time.Minute,
|
||||
},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
_, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
require.True(t, ok)
|
||||
s.FastForward(2 * time.Minute)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: "foo",
|
||||
},
|
||||
want: nil,
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: "spanac",
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "json decode error",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
objectID, err := s.Get(c.(*redisCache[testIndex, string, *testObject]).redisIndexKeys(testIndexID, "one")[0])
|
||||
require.NoError(t, err)
|
||||
s.HSet(objectID, "object", "~~~")
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: "one",
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, server := prepareCache(t, tt.config)
|
||||
tt.preparation(t, c, server)
|
||||
t.Log(server.Keys())
|
||||
|
||||
got, ok := c.Get(tt.args.ctx, tt.args.index, tt.args.key)
|
||||
require.Equal(t, tt.wantOK, ok)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redisCache_Invalidate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
index testIndex
|
||||
key []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
config cache.Config
|
||||
preparation func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis)
|
||||
assertions func(t *testing.T, c cache.Cache[testIndex, string, *testObject])
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "connection error",
|
||||
config: cache.Config{},
|
||||
preparation: func(_ *testing.T, _ cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
s.RequireAuth("foobar")
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"foo"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no keys, noop",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: []string{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalidate by ID",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: []string{"one"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalidate by name",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"foo"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalidate after timeout",
|
||||
config: cache.Config{
|
||||
LastUseAge: time.Minute,
|
||||
},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
_, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
require.True(t, ok)
|
||||
s.FastForward(2 * time.Minute)
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"foo"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"spanac"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, server := prepareCache(t, tt.config)
|
||||
tt.preparation(t, c, server)
|
||||
t.Log(server.Keys())
|
||||
|
||||
err := c.Invalidate(tt.args.ctx, tt.args.index, tt.args.key...)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redisCache_Delete(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
index testIndex
|
||||
key []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
config cache.Config
|
||||
preparation func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis)
|
||||
assertions func(t *testing.T, c cache.Cache[testIndex, string, *testObject])
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "connection error",
|
||||
config: cache.Config{},
|
||||
preparation: func(_ *testing.T, _ cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
s.RequireAuth("foobar")
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"foo"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no keys, noop",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: []string{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete ID",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
// Get be name should still work
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexID,
|
||||
key: []string{"one"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete name",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
// get by ID should still work
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"foo"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "foo")
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
index: testIndexName,
|
||||
key: []string{"spanac"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, server := prepareCache(t, tt.config)
|
||||
tt.preparation(t, c, server)
|
||||
t.Log(server.Keys())
|
||||
|
||||
err := c.Delete(tt.args.ctx, tt.args.index, tt.args.key...)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_redisCache_Truncate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
config cache.Config
|
||||
preparation func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis)
|
||||
assertions func(t *testing.T, c cache.Cache[testIndex, string, *testObject])
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "connection error",
|
||||
config: cache.Config{},
|
||||
preparation: func(_ *testing.T, _ cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
s.RequireAuth("foobar")
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
config: cache.Config{},
|
||||
preparation: func(t *testing.T, c cache.Cache[testIndex, string, *testObject], s *miniredis.Miniredis) {
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "one",
|
||||
Name: []string{"foo", "bar"},
|
||||
})
|
||||
c.Set(context.Background(), &testObject{
|
||||
ID: "two",
|
||||
Name: []string{"Hello", "World"},
|
||||
})
|
||||
},
|
||||
assertions: func(t *testing.T, c cache.Cache[testIndex, string, *testObject]) {
|
||||
obj, ok := c.Get(context.Background(), testIndexID, "one")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
obj, ok = c.Get(context.Background(), testIndexName, "World")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, obj)
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, server := prepareCache(t, tt.config)
|
||||
tt.preparation(t, c, server)
|
||||
t.Log(server.Keys())
|
||||
|
||||
err := c.Truncate(tt.args.ctx)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepareCache(t *testing.T, conf cache.Config) (cache.Cache[testIndex, string, *testObject], *miniredis.Miniredis) {
|
||||
conf.Log = &logging.Config{
|
||||
Level: "debug",
|
||||
AddSource: true,
|
||||
}
|
||||
server := miniredis.RunT(t)
|
||||
server.Select(testDB)
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Network: "tcp",
|
||||
Addr: server.Addr(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
client.Close()
|
||||
server.Close()
|
||||
})
|
||||
connector := NewConnector(Config{
|
||||
Enabled: true,
|
||||
Network: "tcp",
|
||||
Addr: server.Addr(),
|
||||
})
|
||||
c := NewCache[testIndex, string, *testObject](conf, connector, testDB, testIndices)
|
||||
return c, server
|
||||
}
|
Reference in New Issue
Block a user