package service import ( "context" "errors" "testing" "github.com/redis/go-redis/v9" "github.com/topfans/backend/services/moderationService/config" ) // auto_hide_service_test.go - Lua 脚本逻辑测试(spec §7.2) // TestLuaScript 嵌入的 Lua 脚本存在且非空 func TestLuaScript(t *testing.T) { if autoHideLuaScript == "" { t.Fatal("autoHideLuaScript must be embedded") } // 验证 Lua 包含关键逻辑:SETNX, INCR, EXPIRE, threshold requiredKeywords := []string{"SET", "INCR", "EXPIRE"} for _, kw := range requiredKeywords { if !contains(autoHideLuaScript, kw) { t.Errorf("Lua script missing keyword: %s", kw) } } } // TestTryTrigger_LockFailure 测试锁抢占失败场景 // 应用层短锁覆盖整个 auto-hide 流程(spec §6.4) func TestTryTrigger_LockFailure(t *testing.T) { rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) defer rdb.Close() ctx := context.Background() svc := &AutoHideService{redis: rdb, cfg: config.Default} // 用 miniredis 或真实 Redis:先 acquire lock lockKey := "test:mod:report:lock:asset:999" rdb.SetNX(ctx, lockKey, "1", config.Default.LockTTL) // 再调用应该返回 (false, 0, nil) — 锁失败不触发 triggered, _, err := svc.TryTrigger(ctx, "asset", 999, 100) if err != nil { t.Skipf("redis not available: %v", err) } if triggered { t.Errorf("expected triggered=false when lock held") } rdb.Del(ctx, lockKey) } // TestResetCounter_EmptyKeys 测试重置计数器在空 keys 场景 func TestResetCounter_EmptyKeys(t *testing.T) { rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) defer rdb.Close() ctx := context.Background() svc := &AutoHideService{redis: rdb, cfg: config.Default} err := svc.ResetCounter(ctx, "asset", 888) if err != nil && !errors.Is(err, redis.Nil) { t.Logf("ResetCounter err (acceptable for empty): %v", err) } } // TestResetCounter_WithExistingKeys 测试有 keys 时的清理 func TestResetCounter_WithExistingKeys(t *testing.T) { rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) defer rdb.Close() ctx := context.Background() svc := &AutoHideService{redis: rdb, cfg: config.Default} // pre-populate prefix := config.Default.RedisKeyPrefix + ":user:asset:777:" for _, uid := range []int64{101, 102, 103} { key := prefix + itoa(uid) rdb.Set(ctx, key, "1", config.Default.UserMarkerTTL) } counterKey := config.Default.RedisKeyPrefix + ":counter:asset:777" rdb.Set(ctx, counterKey, "3", config.Default.CounterTTL) err := svc.ResetCounter(ctx, "asset", 777) if err != nil && !errors.Is(err, redis.Nil) { t.Logf("ResetCounter err: %v", err) } // verify counter + user markers cleared if exists, _ := rdb.Exists(ctx, counterKey).Result(); exists != 0 { t.Errorf("counter key not cleared") } for _, uid := range []int64{101, 102, 103} { key := prefix + itoa(uid) if exists, _ := rdb.Exists(ctx, key).Result(); exists != 0 { t.Errorf("user marker %d not cleared", uid) } } } func itoa(i int64) string { if i == 0 { return "0" } negative := i < 0 if negative { i = -i } digits := []byte{} for i > 0 { digits = append([]byte{byte('0' + i%10)}, digits...) i /= 10 } if negative { digits = append([]byte{'-'}, digits...) } return string(digits) }