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

246 lines
5.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}