Files
xlgo-core/cache/cache_m10_internal_test.go
2026-07-14 10:24:10 +08:00

200 lines
6.5 KiB
Go

package cache
import (
"context"
"errors"
"testing"
"time"
"github.com/EthanCodeCraft/xlgo-core/database"
"github.com/alicebob/miniredis/v2"
"github.com/redis/go-redis/v9"
)
func setupM10MiniRedis(t *testing.T) *miniredis.Miniredis {
t.Helper()
mr := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{Addr: mr.Addr()})
t.Cleanup(func() { _ = client.Close() })
database.SetTestRedisClient(client)
t.Cleanup(func() { database.SetTestRedisClient(nil) })
return mr
}
func TestM10CacheWritesReturnRedisNotReady(t *testing.T) {
database.SetTestRedisClient(nil)
t.Cleanup(func() { database.SetTestRedisClient(nil) })
c := &redisCache{}
ctx := context.Background()
if err := c.Set(ctx, "k", "v", time.Minute); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Set without Redis err = %v, want ErrRedisNotReady", err)
}
if err := c.Delete(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Delete without Redis err = %v, want ErrRedisNotReady", err)
}
if err := c.DeleteByPattern(ctx, "k:*"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("DeleteByPattern without Redis err = %v, want ErrRedisNotReady", err)
}
}
func TestM10RawHelpersReturnRedisNotReady(t *testing.T) {
database.SetTestRedisClient(nil)
t.Cleanup(func() { database.SetTestRedisClient(nil) })
ctx := context.Background()
if _, err := Incr(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Incr without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := IncrBy(ctx, "k", 2); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("IncrBy without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := Decr(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Decr without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := GetTTL(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("GetTTL without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := SetExpire(ctx, "k", time.Minute); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("SetExpire without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := GetRaw(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("GetRaw without Redis err = %v, want ErrRedisNotReady", err)
}
if err := SetRaw(ctx, "k", "v", time.Minute); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("SetRaw without Redis err = %v, want ErrRedisNotReady", err)
}
}
func TestM10ExistsReturnsBackendErrors(t *testing.T) {
setupM10MiniRedis(t)
c := &redisCache{}
ctx := context.Background()
exists, err := c.Exists(ctx, "missing")
if err != nil {
t.Fatalf("Exists missing err = %v", err)
}
if exists {
t.Fatal("Exists missing = true, want false")
}
if err := c.Set(ctx, "present", "value", time.Minute); err != nil {
t.Fatalf("Set present: %v", err)
}
exists, err = c.Exists(ctx, "present")
if err != nil || !exists {
t.Fatalf("Exists present = %v, err=%v; want true,nil", exists, err)
}
canceled, cancel := context.WithCancel(ctx)
cancel()
if exists, err = c.Exists(canceled, "present"); err == nil || exists {
t.Fatalf("Exists canceled = %v, err=%v; want false,error", exists, err)
}
}
func TestM10GetReturnsBackendAndDecodeErrors(t *testing.T) {
setupM10MiniRedis(t)
c := &redisCache{}
ctx := context.Background()
var got string
ok, err := c.Get(ctx, "missing", &got)
if err != nil || ok {
t.Fatalf("Get missing = %v, err=%v; want false,nil", ok, err)
}
if err := c.Set(ctx, "present", "value", time.Minute); err != nil {
t.Fatalf("Set present: %v", err)
}
ok, err = c.Get(ctx, "present", &got)
if err != nil || !ok || got != "value" {
t.Fatalf("Get present = %v, %q, err=%v; want true,value,nil", ok, got, err)
}
if err := c.client().Set(ctx, "bad-json", "{", time.Minute).Err(); err != nil {
t.Fatalf("Set bad-json: %v", err)
}
ok, err = c.Get(ctx, "bad-json", &got)
if err == nil || ok {
t.Fatalf("Get bad-json = %v, err=%v; want false,error", ok, err)
}
canceled, cancel := context.WithCancel(ctx)
cancel()
ok, err = c.Get(canceled, "present", &got)
if err == nil || ok {
t.Fatalf("Get canceled = %v, err=%v; want false,error", ok, err)
}
}
func TestM10PackageExistsReturnsBackendErrors(t *testing.T) {
setupM10MiniRedis(t)
orig := GetDefaultCache()
t.Cleanup(func() { SetDefaultCacheManager(orig) })
SetDefaultCacheManager(NewCacheManager())
Init()
ctx := context.Background()
if err := GetCache().Set(ctx, "present", "value", time.Minute); err != nil {
t.Fatalf("Set through facade: %v", err)
}
exists, err := Exists(ctx, "present")
if err != nil || !exists {
t.Fatalf("facade Exists = %v, err=%v; want true,nil", exists, err)
}
}
func TestM10PackageGetReturnsBackendErrors(t *testing.T) {
setupM10MiniRedis(t)
orig := GetDefaultCache()
t.Cleanup(func() { SetDefaultCacheManager(orig) })
SetDefaultCacheManager(NewCacheManager())
Init()
ctx := context.Background()
if err := GetCache().Set(ctx, "present", "value", time.Minute); err != nil {
t.Fatalf("Set through facade: %v", err)
}
var got string
ok, err := Get(ctx, "present", &got)
if err != nil || !ok || got != "value" {
t.Fatalf("facade Get = %v, %q, err=%v; want true,value,nil", ok, got, err)
}
}
// TestM10CustomCacheServiceCompile asserts a user-provided CacheService
// implementation compiles against the (bool, error) contract. This is a
// compile-time guard: if the interface changes again, this stops building.
type customCacheSvc struct{}
func (customCacheSvc) Get(ctx context.Context, key string, dest any) (bool, error) { return false, nil }
func (customCacheSvc) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
return nil
}
func (customCacheSvc) Delete(ctx context.Context, key string) error { return nil }
func (customCacheSvc) DeleteByPattern(ctx context.Context, p string) error { return nil }
func (customCacheSvc) Exists(ctx context.Context, key string) (bool, error) { return false, nil }
func TestM10CustomCacheServiceCompiles(t *testing.T) {
var _ CacheService = customCacheSvc{}
}
// TestM10RedisNotReadyOnGetExists asserts the Redis-not-ready error is
// surfaced (not swallowed) when no backend is configured.
func TestM10RedisNotReadyOnGetExists(t *testing.T) {
database.SetTestRedisClient(nil)
t.Cleanup(func() { database.SetTestRedisClient(nil) })
c := &redisCache{}
ctx := context.Background()
if _, err := c.Get(ctx, "k", new(string)); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Get without Redis err = %v, want ErrRedisNotReady", err)
}
if _, err := c.Exists(ctx, "k"); !errors.Is(err, ErrRedisNotReady) {
t.Fatalf("Exists without Redis err = %v, want ErrRedisNotReady", err)
}
}