Files
user-system/internal/cache/cache_test.go

246 lines
5.9 KiB
Go
Raw Normal View History

package cache_test
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/user-management-system/internal/cache"
)
// TestRedisCache_Disabled 测试禁用状态的RedisCache不报错
func TestRedisCache_Disabled(t *testing.T) {
c := cache.NewRedisCache(false)
ctx := context.Background()
if err := c.Set(ctx, "key", "value", time.Minute); err != nil {
t.Errorf("disabled cache Set should not error: %v", err)
}
val, err := c.Get(ctx, "key")
if err != nil {
t.Errorf("disabled cache Get should not error: %v", err)
}
if val != nil {
t.Errorf("disabled cache Get should return nil, got: %v", val)
}
if err := c.Delete(ctx, "key"); err != nil {
t.Errorf("disabled cache Delete should not error: %v", err)
}
exists, err := c.Exists(ctx, "key")
if err != nil {
t.Errorf("disabled cache Exists should not error: %v", err)
}
if exists {
t.Error("disabled cache Exists should return false")
}
if err := c.Clear(ctx); err != nil {
t.Errorf("disabled cache Clear should not error: %v", err)
}
if err := c.Close(); err != nil {
t.Errorf("disabled cache Close should not error: %v", err)
}
}
// TestL1Cache_SetGet 测试L1内存缓存的基本读写
func TestL1Cache_SetGet(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("user:1", "alice", time.Minute)
val, ok := l1.Get("user:1")
if !ok {
t.Fatal("L1 Get: expected hit")
}
if val != "alice" {
t.Errorf("L1 Get value = %v, want alice", val)
}
}
// TestL1Cache_Expiration 测试L1缓存过期
func TestL1Cache_Expiration(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("expire:1", "v", 50*time.Millisecond)
time.Sleep(100 * time.Millisecond)
_, ok := l1.Get("expire:1")
if ok {
t.Error("L1 key should have expired")
}
}
// TestL1Cache_Delete 测试L1缓存删除
func TestL1Cache_Delete(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("del:1", "v", time.Minute)
l1.Delete("del:1")
_, ok := l1.Get("del:1")
if ok {
t.Error("L1 key should be deleted")
}
}
// TestL1Cache_Clear 测试L1缓存清空
func TestL1Cache_Clear(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("a", 1, time.Minute)
l1.Set("b", 2, time.Minute)
l1.Clear()
_, ok1 := l1.Get("a")
_, ok2 := l1.Get("b")
if ok1 || ok2 {
t.Error("L1 cache should be empty after Clear()")
}
}
// TestL1Cache_Size 测试L1缓存大小统计
func TestL1Cache_Size(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("s1", 1, time.Minute)
l1.Set("s2", 2, time.Minute)
l1.Set("s3", 3, time.Minute)
if l1.Size() != 3 {
t.Errorf("L1 Size = %d, want 3", l1.Size())
}
l1.Delete("s1")
if l1.Size() != 2 {
t.Errorf("L1 Size after Delete = %d, want 2", l1.Size())
}
}
// TestL1Cache_Cleanup 测试L1过期键清理
func TestL1Cache_Cleanup(t *testing.T) {
l1 := cache.NewL1Cache()
l1.Set("exp", "v", 30*time.Millisecond)
l1.Set("keep", "v", time.Minute)
time.Sleep(60 * time.Millisecond)
l1.Cleanup()
if l1.Size() != 1 {
t.Errorf("after Cleanup L1 Size = %d, want 1", l1.Size())
}
}
// TestCacheManager_SetGet 测试CacheManager读写仅L1
func TestCacheManager_SetGet(t *testing.T) {
l1 := cache.NewL1Cache()
cm := cache.NewCacheManager(l1, nil)
ctx := context.Background()
if err := cm.Set(ctx, "k1", "v1", time.Minute, time.Minute); err != nil {
t.Fatalf("CacheManager Set error: %v", err)
}
val, ok := cm.Get(ctx, "k1")
if !ok {
t.Fatal("CacheManager Get: expected hit")
}
if val != "v1" {
t.Errorf("CacheManager Get value = %v, want v1", val)
}
}
// TestCacheManager_Delete 测试CacheManager删除
func TestCacheManager_Delete(t *testing.T) {
l1 := cache.NewL1Cache()
cm := cache.NewCacheManager(l1, nil)
ctx := context.Background()
_ = cm.Set(ctx, "del:1", "v", time.Minute, time.Minute)
if err := cm.Delete(ctx, "del:1"); err != nil {
t.Fatalf("CacheManager Delete error: %v", err)
}
_, ok := cm.Get(ctx, "del:1")
if ok {
t.Error("CacheManager key should be deleted")
}
}
// TestCacheManager_Exists 测试CacheManager存在性检查
func TestCacheManager_Exists(t *testing.T) {
l1 := cache.NewL1Cache()
cm := cache.NewCacheManager(l1, nil)
ctx := context.Background()
if cm.Exists(ctx, "notexist") {
t.Error("CacheManager Exists should return false for missing key")
}
_ = cm.Set(ctx, "exist:1", "v", time.Minute, time.Minute)
if !cm.Exists(ctx, "exist:1") {
t.Error("CacheManager Exists should return true after Set")
}
}
// TestCacheManager_Clear 测试CacheManager清空
func TestCacheManager_Clear(t *testing.T) {
l1 := cache.NewL1Cache()
cm := cache.NewCacheManager(l1, nil)
ctx := context.Background()
_ = cm.Set(ctx, "a", 1, time.Minute, time.Minute)
_ = cm.Set(ctx, "b", 2, time.Minute, time.Minute)
if err := cm.Clear(ctx); err != nil {
t.Fatalf("CacheManager Clear error: %v", err)
}
if cm.Exists(ctx, "a") || cm.Exists(ctx, "b") {
t.Error("CacheManager should be empty after Clear()")
}
}
// TestCacheManager_Concurrent 测试CacheManager并发安全
func TestCacheManager_Concurrent(t *testing.T) {
l1 := cache.NewL1Cache()
cm := cache.NewCacheManager(l1, nil)
ctx := context.Background()
var wg sync.WaitGroup
var hitCount int64
// 预热
_ = cm.Set(ctx, "concurrent:key", "v", time.Minute, time.Minute)
// 并发读写
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 20; j++ {
if _, ok := cm.Get(ctx, "concurrent:key"); ok {
atomic.AddInt64(&hitCount, 1)
}
}
}()
}
wg.Wait()
if hitCount == 0 {
t.Error("concurrent cache reads should produce hits")
}
}
// TestCacheManager_WithDisabledL2 测试CacheManager配合禁用L2
func TestCacheManager_WithDisabledL2(t *testing.T) {
l1 := cache.NewL1Cache()
l2 := cache.NewRedisCache(false) // disabled
cm := cache.NewCacheManager(l1, l2)
ctx := context.Background()
if err := cm.Set(ctx, "k", "v", time.Minute, time.Minute); err != nil {
t.Fatalf("Set with disabled L2 should not error: %v", err)
}
val, ok := cm.Get(ctx, "k")
if !ok || val != "v" {
t.Errorf("Get from L1 after Set = (%v, %v), want (v, true)", val, ok)
}
}