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) } }