package weekstar import ( "context" "encoding/json" "testing" "time" "chatapp3-golang/internal/model" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" ) func TestCurrentRankingReturnsLiveRank(t *testing.T) { ctx := context.Background() service, db := newTestWeekStarService(t) if err := db.AutoMigrate(&model.UserBaseInfo{}); err != nil { t.Fatalf("auto migrate user_base_info: %v", err) } redisServer := miniredis.RunT(t) redisClient := redis.NewClient(&redis.Options{Addr: redisServer.Addr()}) defer redisClient.Close() service.repo.Redis = redisClient now := time.Now().In(service.location) cycleStart, cycleEnd := service.cycleBounds(now) configRow := model.WeekStarActivityConfig{ ID: 101, SysOrigin: "LIKEI", Enabled: true, StartAt: service.toStorageWallClock(cycleStart.Add(-time.Hour)), EndAt: service.toStorageWallClock(cycleEnd.Add(time.Hour)), Timezone: "Asia/Riyadh", CreateTime: now, UpdateTime: now, } if err := db.Create(&configRow).Error; err != nil { t.Fatalf("create config: %v", err) } if err := db.Create(&model.WeekStarActivityGiftConfig{ ConfigID: configRow.ID, GiftID: 2001, GiftName: "Gift A", GiftCandy: 100, Sort: 1, }).Error; err != nil { t.Fatalf("create gift config: %v", err) } if err := db.Create([]model.UserBaseInfo{ {ID: 3001, Account: "3001", UserNickname: "Alice", CountryCode: "SA", CountryName: "Saudi Arabia"}, {ID: 3002, Account: "3002", UserNickname: "Bob", CountryCode: "AE", CountryName: "UAE"}, }).Error; err != nil { t.Fatalf("create users: %v", err) } if err := redisClient.ZAdd(ctx, service.rankRedisKey(configRow.ID, service.cycleKey(cycleStart)), redis.Z{Member: "3001", Score: 250}, redis.Z{Member: "3002", Score: 500}, ).Err(); err != nil { t.Fatalf("zadd rank: %v", err) } resp, err := service.CurrentRanking(ctx, "likei", 20) if err != nil { t.Fatalf("CurrentRanking() error = %v", err) } if resp.ActivityStatus != weekStarActivityStatusOngoing { t.Fatalf("ActivityStatus = %s, want %s", resp.ActivityStatus, weekStarActivityStatusOngoing) } if resp.ConfigID != configRow.ID { t.Fatalf("ConfigID = %d, want %d", resp.ConfigID, configRow.ID) } if resp.CycleKey != service.cycleKey(cycleStart) { t.Fatalf("CycleKey = %s, want %s", resp.CycleKey, service.cycleKey(cycleStart)) } if len(resp.GiftConfigs) != 1 || resp.GiftConfigs[0].GiftID != 2001 { t.Fatalf("GiftConfigs = %#v", resp.GiftConfigs) } if len(resp.Records) != 2 { t.Fatalf("Records length = %d, want 2", len(resp.Records)) } if resp.Records[0].UserID != 3002 || resp.Records[0].Rank != 1 || resp.Records[0].ScoreGold != 500 { t.Fatalf("top record = %#v", resp.Records[0]) } if resp.Records[1].UserID != 3001 || resp.Records[1].Rank != 2 || resp.Records[1].ScoreGold != 250 { t.Fatalf("second record = %#v", resp.Records[1]) } } func TestCurrentRankingFallsBackToGiftLogsWhenRedisEmpty(t *testing.T) { ctx := context.Background() service, db := newTestWeekStarService(t) if err := db.AutoMigrate(&model.UserBaseInfo{}, &model.WeekStarGiftLog{}); err != nil { t.Fatalf("auto migrate ranking dependencies: %v", err) } redisServer := miniredis.RunT(t) redisClient := redis.NewClient(&redis.Options{Addr: redisServer.Addr()}) defer redisClient.Close() service.repo.Redis = redisClient now := time.Now().In(service.location) cycleStart, cycleEnd := service.cycleBounds(now) cycleKey := service.cycleKey(cycleStart) configRow := model.WeekStarActivityConfig{ ID: 102, SysOrigin: "LIKEI", Enabled: true, StartAt: service.toStorageWallClock(cycleStart.Add(-time.Hour)), EndAt: service.toStorageWallClock(cycleEnd.Add(time.Hour)), Timezone: "Asia/Riyadh", CreateTime: now, UpdateTime: now, } if err := db.Create(&configRow).Error; err != nil { t.Fatalf("create config: %v", err) } if err := db.Create(&model.WeekStarActivityGiftConfig{ ConfigID: configRow.ID, GiftID: 2001, GiftName: "Gift A", GiftCandy: 100, Sort: 1, }).Error; err != nil { t.Fatalf("create gift config: %v", err) } if err := db.Create([]model.UserBaseInfo{ {ID: 3001, Account: "3001", UserNickname: "Alice", CountryCode: "SA", CountryName: "Saudi Arabia"}, {ID: 3002, Account: "3002", UserNickname: "Bob", CountryCode: "AE", CountryName: "UAE"}, }).Error; err != nil { t.Fatalf("create users: %v", err) } if err := db.Create([]model.WeekStarGiftLog{ {ID: 1, ConfigID: configRow.ID, CycleKey: cycleKey, TrackID: "track-1", SendUserID: 3001, GiftID: 2001, Quantity: 1, AcceptUserSize: 1, ScoreGold: 250, EventTime: now, Status: weekStarGiftLogStatusSuccess, CreateTime: now, UpdateTime: now}, {ID: 2, ConfigID: configRow.ID, CycleKey: cycleKey, TrackID: "track-2", SendUserID: 3002, GiftID: 2001, Quantity: 1, AcceptUserSize: 1, ScoreGold: 400, EventTime: now, Status: weekStarGiftLogStatusSuccess, CreateTime: now, UpdateTime: now}, {ID: 3, ConfigID: configRow.ID, CycleKey: cycleKey, TrackID: "track-3", SendUserID: 3002, GiftID: 2001, Quantity: 1, AcceptUserSize: 1, ScoreGold: 100, EventTime: now, Status: weekStarGiftLogStatusSuccess, CreateTime: now, UpdateTime: now}, {ID: 4, ConfigID: configRow.ID, CycleKey: cycleKey, TrackID: "track-4", SendUserID: 3001, GiftID: 2001, Quantity: 1, AcceptUserSize: 1, ScoreGold: 999, EventTime: now, Status: weekStarGiftLogStatusFailed, CreateTime: now, UpdateTime: now}, }).Error; err != nil { t.Fatalf("create gift logs: %v", err) } resp, err := service.CurrentRanking(ctx, "LIKEI", 20) if err != nil { t.Fatalf("CurrentRanking() error = %v", err) } if len(resp.Records) != 2 { t.Fatalf("Records length = %d, want 2", len(resp.Records)) } if resp.Records[0].UserID != 3002 || resp.Records[0].Rank != 1 || resp.Records[0].ScoreGold != 500 { t.Fatalf("top record = %#v", resp.Records[0]) } if resp.Records[1].UserID != 3001 || resp.Records[1].Rank != 2 || resp.Records[1].ScoreGold != 250 { t.Fatalf("second record = %#v", resp.Records[1]) } } func TestProcessGiftEventCountsConfiguredNonDefaultSysOrigin(t *testing.T) { ctx := context.Background() service, db := newTestWeekStarService(t) if err := db.AutoMigrate(&model.WeekStarGiftLog{}); err != nil { t.Fatalf("auto migrate gift log: %v", err) } redisServer := miniredis.RunT(t) redisClient := redis.NewClient(&redis.Options{Addr: redisServer.Addr()}) defer redisClient.Close() service.repo.Redis = redisClient now := time.Now().In(service.location) cycleStart, cycleEnd := service.cycleBounds(now) configRow := model.WeekStarActivityConfig{ ID: 103, SysOrigin: "TARAB", Enabled: true, StartAt: service.toStorageWallClock(cycleStart.Add(-time.Hour)), EndAt: service.toStorageWallClock(cycleEnd.Add(time.Hour)), Timezone: "Asia/Riyadh", CreateTime: now, UpdateTime: now, } if err := db.Create(&configRow).Error; err != nil { t.Fatalf("create config: %v", err) } if err := db.Create(&model.WeekStarActivityGiftConfig{ ConfigID: configRow.ID, GiftID: 2001, GiftName: "Gift A", GiftCandy: 100, Sort: 1, }).Error; err != nil { t.Fatalf("create gift config: %v", err) } err := service.ProcessGiftEvent(ctx, weekStarGiftEvent{ TrackID: flexibleString("tarab-track-1"), SysOrigin: "TARAB", SendUserID: flexibleInt64(3001), Quantity: flexibleInt64(2), CreateTime: flexibleInt64(now.UnixMilli()), Accepts: []weekStarGiftEventUser{ {AcceptUserID: flexibleInt64(4001)}, }, GiftConfig: weekStarGiftEventGift{ ID: flexibleInt64(2001), GiftCandy: flexibleInt64(100), GiftName: "Gift A", }, }, `{"trackId":"tarab-track-1"}`) if err != nil { t.Fatalf("ProcessGiftEvent() error = %v", err) } score, err := redisClient.ZScore(ctx, service.rankRedisKey(configRow.ID, service.cycleKey(cycleStart)), "3001").Result() if err != nil { t.Fatalf("ZScore() error = %v", err) } if score != 200 { t.Fatalf("score = %v, want 200", score) } var count int64 if err := db.Model(&model.WeekStarGiftLog{}). Where("config_id = ? AND cycle_key = ? AND send_user_id = ? AND status = ?", configRow.ID, service.cycleKey(cycleStart), 3001, weekStarGiftLogStatusSuccess). Count(&count).Error; err != nil { t.Fatalf("count gift log: %v", err) } if count != 1 { t.Fatalf("success gift log count = %d, want 1", count) } } func TestProcessGiftPayloadUnwrapsJavaMessageEventEnvelope(t *testing.T) { ctx := context.Background() service, db := newTestWeekStarService(t) service.cfg.WeekStar.RocketMQ.Tag = "give_gift_v3" if err := db.AutoMigrate(&model.WeekStarGiftLog{}); err != nil { t.Fatalf("auto migrate gift log: %v", err) } redisServer := miniredis.RunT(t) redisClient := redis.NewClient(&redis.Options{Addr: redisServer.Addr()}) defer redisClient.Close() service.repo.Redis = redisClient now := time.Now().In(service.location) cycleStart, cycleEnd := service.cycleBounds(now) configRow := model.WeekStarActivityConfig{ ID: 104, SysOrigin: "LIKEI", Enabled: true, StartAt: service.toStorageWallClock(cycleStart.Add(-time.Hour)), EndAt: service.toStorageWallClock(cycleEnd.Add(time.Hour)), Timezone: "Asia/Riyadh", CreateTime: now, UpdateTime: now, } if err := db.Create(&configRow).Error; err != nil { t.Fatalf("create config: %v", err) } if err := db.Create(&model.WeekStarActivityGiftConfig{ ConfigID: configRow.ID, GiftID: 2044977670327955458, GiftName: "Like", GiftCandy: 100, Sort: 1, }).Error; err != nil { t.Fatalf("create gift config: %v", err) } body, err := json.Marshal(map[string]any{ "trackId": int64(2049831533035974657), "sysOrigin": "LIKEI", "sendUserId": "2042274349343506434", "quantity": 4, "createTime": now.UnixMilli(), "accepts": []map[string]any{ {"acceptUserId": "2042274349343506434"}, }, "giftConfig": map[string]any{ "id": "2044977670327955458", "giftCandy": "100.00", "giftName": "Like", }, }) if err != nil { t.Fatalf("marshal body: %v", err) } payload, err := json.Marshal(map[string]any{ "consumeId": "2049831533035974657", "topic": "RC_DEFAULT_APP_ORDINARY", "tag": "give_gift_v3", "body": string(body), }) if err != nil { t.Fatalf("marshal payload: %v", err) } if err := service.ProcessGiftPayload(ctx, string(payload)); err != nil { t.Fatalf("ProcessGiftPayload() error = %v", err) } score, err := redisClient.ZScore(ctx, service.rankRedisKey(configRow.ID, service.cycleKey(cycleStart)), "2042274349343506434").Result() if err != nil { t.Fatalf("ZScore() error = %v", err) } if score != 400 { t.Fatalf("score = %v, want 400", score) } var count int64 if err := db.Model(&model.WeekStarGiftLog{}). Where("config_id = ? AND send_user_id = ? AND status = ?", configRow.ID, int64(2042274349343506434), weekStarGiftLogStatusSuccess). Count(&count).Error; err != nil { t.Fatalf("count gift log: %v", err) } if count != 1 { t.Fatalf("success gift log count = %d, want 1", count) } } func TestCurrentRankingReturnsDisabledWhenNoActiveConfig(t *testing.T) { service, _ := newTestWeekStarService(t) resp, err := service.CurrentRanking(context.Background(), "LIKEI", 20) if err != nil { t.Fatalf("CurrentRanking() error = %v", err) } if resp.ActivityStatus != weekStarActivityStatusDisabled { t.Fatalf("ActivityStatus = %s, want %s", resp.ActivityStatus, weekStarActivityStatusDisabled) } if len(resp.Records) != 0 { t.Fatalf("Records length = %d, want 0", len(resp.Records)) } }