118 lines
4.0 KiB
Go
118 lines
4.0 KiB
Go
package userleaderboard
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestQueryWindowUsesExistingUTCBoundaries(t *testing.T) {
|
|
now := time.Date(2026, 5, 20, 15, 30, 0, 0, time.FixedZone("CST", 8*60*60))
|
|
|
|
todayStart, todayEnd := QueryWindow(PeriodToday, now)
|
|
if todayStart != time.Date(2026, 5, 20, 0, 0, 0, 0, time.UTC) {
|
|
t.Fatalf("today start = %s", todayStart)
|
|
}
|
|
if !todayEnd.Equal(now.UTC()) {
|
|
t.Fatalf("today end = %s", todayEnd)
|
|
}
|
|
|
|
weekStart, _ := QueryWindow(PeriodWeek, now)
|
|
if weekStart != time.Date(2026, 5, 18, 0, 0, 0, 0, time.UTC) {
|
|
t.Fatalf("week start = %s", weekStart)
|
|
}
|
|
|
|
monthStart, _ := QueryWindow(PeriodMonth, now)
|
|
if monthStart != time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC) {
|
|
t.Fatalf("month start = %s", monthStart)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAliases(t *testing.T) {
|
|
if NormalizeBoardType("receiver") != BoardReceived {
|
|
t.Fatal("receiver should normalize to received")
|
|
}
|
|
if NormalizeBoardType("gift_sent") != BoardSent {
|
|
t.Fatal("gift_sent should normalize to sent")
|
|
}
|
|
if NormalizeBoardType("gaming") != BoardGame {
|
|
t.Fatal("gaming should normalize to game")
|
|
}
|
|
if NormalizePeriod("monthly") != PeriodMonth {
|
|
t.Fatal("monthly should normalize to month")
|
|
}
|
|
}
|
|
|
|
func TestStoreKeyPrefixAndBucketAreStable(t *testing.T) {
|
|
store := NewStore(nil, "activity:user_leaderboard:")
|
|
weekStart := time.Date(2026, 12, 28, 0, 0, 0, 0, time.UTC)
|
|
|
|
if got := store.scoreKey(" HyApp_Prod ", BoardSent, PeriodWeek, weekStart); got != "activity:user_leaderboard:hyapp_prod:sent:week:202653:scores" {
|
|
t.Fatalf("score key mismatch: %s", got)
|
|
}
|
|
if got := store.itemKey("hyapp_prod", BoardReceived, PeriodToday, time.Date(2026, 6, 26, 0, 0, 0, 0, time.UTC), "10001"); got != "activity:user_leaderboard:hyapp_prod:received:today:20260626:users:10001" {
|
|
t.Fatalf("item key mismatch: %s", got)
|
|
}
|
|
if got := store.scoreKey("huwaa", BoardGame, PeriodMonth, time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC)); got != "activity:user_leaderboard:huwaa:game:month:202607:scores" {
|
|
t.Fatalf("game score key mismatch: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestApplyGameEventUsesDedicatedRedisBucket(t *testing.T) {
|
|
redisAddr := os.Getenv("HYAPP_TEST_REDIS_ADDR")
|
|
if redisAddr == "" {
|
|
t.Skip("HYAPP_TEST_REDIS_ADDR is not set")
|
|
}
|
|
ctx := context.Background()
|
|
client, err := NewRedisClient(ctx, redisAddr, "", 0)
|
|
if err != nil {
|
|
t.Fatalf("connect test Redis: %v", err)
|
|
}
|
|
defer client.Close()
|
|
prefix := fmt.Sprintf("test:user_leaderboard:%d", time.Now().UnixNano())
|
|
defer func() {
|
|
keys, _, scanErr := client.Scan(ctx, 0, prefix+":*", 100).Result()
|
|
if scanErr == nil && len(keys) > 0 {
|
|
_ = client.Del(ctx, keys...).Err()
|
|
}
|
|
}()
|
|
store := NewStore(client, prefix)
|
|
occurredAt := time.Date(2026, 7, 15, 12, 0, 0, 0, time.UTC)
|
|
event := GameEvent{
|
|
AppCode: "huwaa",
|
|
EventID: "game_level:test_order_1",
|
|
UserID: 20002,
|
|
CoinSpent: 120,
|
|
OccurredAtMS: occurredAt.UnixMilli(),
|
|
}
|
|
|
|
applied, err := store.ApplyGameEvent(ctx, event)
|
|
if err != nil || !applied {
|
|
t.Fatalf("first game event should apply: applied=%v err=%v", applied, err)
|
|
}
|
|
applied, err = store.ApplyGameEvent(ctx, event)
|
|
if err != nil || applied {
|
|
t.Fatalf("duplicate game event should no-op: applied=%v err=%v", applied, err)
|
|
}
|
|
page, err := store.List(ctx, Query{
|
|
AppCode: "huwaa",
|
|
BoardType: BoardGame,
|
|
Period: PeriodToday,
|
|
Page: 1,
|
|
PageSize: 20,
|
|
Now: occurredAt.Add(time.Minute),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("list game leaderboard: %v", err)
|
|
}
|
|
if page.BoardType != BoardGame || page.Total != 1 || len(page.Items) != 1 || page.Items[0].UserID != "20002" || page.Items[0].GiftValue != 120 || page.Items[0].GiftCount != 0 || page.Items[0].TransactionCount != 1 || page.Items[0].LastGiftAtMS != occurredAt.UnixMilli() {
|
|
t.Fatalf("game leaderboard item mismatch: %+v", page)
|
|
}
|
|
sent, err := store.List(ctx, Query{AppCode: "huwaa", BoardType: BoardSent, Period: PeriodToday, Page: 1, PageSize: 20, Now: occurredAt.Add(time.Minute)})
|
|
if err != nil || sent.Total != 0 {
|
|
t.Fatalf("game event must not leak into sent board: page=%+v err=%v", sent, err)
|
|
}
|
|
}
|