126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/cache"
|
|
goredis "github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
type fakeClient struct {
|
|
goredis.Cmdable
|
|
key string
|
|
ttl time.Duration
|
|
failure error
|
|
}
|
|
|
|
func (f *fakeClient) Get(ctx context.Context, key string) *goredis.StringCmd {
|
|
f.key = key
|
|
return goredis.NewStringResult("", f.failure)
|
|
}
|
|
func (f *fakeClient) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) *goredis.StatusCmd {
|
|
f.key, f.ttl = key, ttl
|
|
return goredis.NewStatusResult("OK", f.failure)
|
|
}
|
|
func (f *fakeClient) Del(ctx context.Context, keys ...string) *goredis.IntCmd {
|
|
f.key = keys[0]
|
|
return goredis.NewIntResult(1, f.failure)
|
|
}
|
|
|
|
func TestAdapterPreservesErrorsAndExpiration(t *testing.T) {
|
|
f := &fakeClient{failure: goredis.Nil}
|
|
store, _ := New(f, "app:")
|
|
if _, err := store.Get(context.Background(), "key"); !errors.Is(err, cache.ErrMiss) || f.key != "app:key" {
|
|
t.Fatal(err, f.key)
|
|
}
|
|
f.failure = errors.New("unavailable")
|
|
if _, err := store.Get(context.Background(), "key"); !errors.Is(err, f.failure) {
|
|
t.Fatal(err)
|
|
}
|
|
f.failure = nil
|
|
if err := store.Set(context.Background(), "key", []byte("value"), time.Nanosecond); err != nil || f.ttl != time.Millisecond {
|
|
t.Fatal(err, f.ttl)
|
|
}
|
|
if err := store.Set(context.Background(), "key", nil, -time.Second); err == nil {
|
|
t.Fatal("negative TTL accepted")
|
|
}
|
|
if err := store.Delete(context.Background(), "key"); err != nil || f.key != "app:key" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRedisIntegration(t *testing.T) {
|
|
address := os.Getenv("GO_OMNI_REDIS_TEST_ADDR")
|
|
if address == "" {
|
|
t.Skip("set GO_OMNI_REDIS_TEST_ADDR to an isolated test Redis")
|
|
}
|
|
client := goredis.NewClient(&goredis.Options{Addr: address, PoolSize: 2, MaxRetries: -1, ContextTimeoutEnabled: true, DialTimeout: time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second})
|
|
defer client.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
if err := client.Ping(ctx).Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
prefix := fmt.Sprintf("goOmniTest:%d:", time.Now().UnixNano())
|
|
store, _ := New(client, prefix)
|
|
defer store.Delete(context.Background(), "key", "forever", "list", "canceled")
|
|
if err := store.Set(ctx, "key", []byte{0, 1, 255}, 80*time.Millisecond); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v, err := store.Get(ctx, "key")
|
|
if err != nil || len(v) != 3 || v[2] != 255 {
|
|
t.Fatal(v, err)
|
|
}
|
|
deadline := time.Now().Add(time.Second)
|
|
for {
|
|
_, err := store.Get(ctx, "key")
|
|
if errors.Is(err, cache.ErrMiss) {
|
|
break
|
|
}
|
|
if err != nil || time.Now().After(deadline) {
|
|
t.Fatal("TTL failed", err)
|
|
}
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|
|
if err := store.Set(ctx, "forever", []byte("ok"), 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ttl := client.PTTL(ctx, prefix+"forever").Val(); ttl != -1 {
|
|
t.Fatalf("unexpected persistent TTL %v", ttl)
|
|
}
|
|
if err := store.Delete(ctx, "forever"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.Get(ctx, "forever"); !errors.Is(err, cache.ErrMiss) {
|
|
t.Fatal(err)
|
|
}
|
|
if err := client.RPush(ctx, prefix+"list", "value").Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.Get(ctx, "list"); err == nil || errors.Is(err, cache.ErrMiss) {
|
|
t.Fatal("Redis error treated as miss", err)
|
|
}
|
|
canceled, stop := context.WithCancel(ctx)
|
|
stop()
|
|
if err := store.Set(canceled, "canceled", []byte("value"), 0); !errors.Is(err, context.Canceled) {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.Get(ctx, "canceled"); !errors.Is(err, cache.ErrMiss) {
|
|
t.Fatal("canceled write reached Redis", err)
|
|
}
|
|
if err := store.Delete(ctx, "list"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := client.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.Get(ctx, "key"); err == nil || errors.Is(err, cache.ErrMiss) {
|
|
t.Fatal("closed client treated as miss", err)
|
|
}
|
|
}
|