233 lines
9.3 KiB
Go
233 lines
9.3 KiB
Go
package userbadge
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/common"
|
|
"chatapp3-golang/internal/model"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestCurrentBadgesSplitLongAndShort(t *testing.T) {
|
|
db := newUserBadgeTestDB(t)
|
|
now := time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
|
|
userID := int64(42)
|
|
if err := db.Create(&model.UserVipState{
|
|
ID: 1,
|
|
SysOrigin: "LIKEI",
|
|
UserID: userID,
|
|
Level: 2,
|
|
LevelCode: "VIP2",
|
|
DisplayName: "VIP 2",
|
|
Status: statusActive,
|
|
StartAt: now.Add(-time.Hour),
|
|
ExpireAt: now.Add(24 * time.Hour),
|
|
BadgeResourceID: 101,
|
|
}).Error; err != nil {
|
|
t.Fatalf("create vip state: %v", err)
|
|
}
|
|
seedBadgeConfig(t, db, 101, "ACTIVITY", "VIP Badge", "VIP_BADGE", 5)
|
|
seedBadgeConfig(t, db, 102, badgeSourceTypeActivity, "Activity Badge", "ACTIVITY_BADGE", 3)
|
|
seedBadgeConfig(t, db, 103, badgeSourceTypeAchievement, "Achievement Badge", "ACHIEVEMENT_BADGE", 7)
|
|
seedBadgeConfig(t, db, 104, "ADMINISTRATOR", "Admin Badge", "ADMIN_BADGE", 0)
|
|
seedBadgeConfig(t, db, 105, badgeSourceTypeActivity, "Expired Badge", "EXPIRED_BADGE", 0)
|
|
seedBadgeConfig(t, db, 106, badgeSourceTypeActivity, "Unused Badge", "UNUSED_BADGE", 0)
|
|
for _, badgeID := range []int64{101, 102, 103, 104, 105, 106} {
|
|
if err := db.Create(&model.SysBadgePictureConfig{
|
|
ID: badgeID + 1000,
|
|
BadgeConfigID: badgeID,
|
|
SysOrigin: "LIKEI",
|
|
SelectURL: "https://cdn.example.com/" + formatID(badgeID) + ".png",
|
|
NotSelectURL: "https://cdn.example.com/" + formatID(badgeID) + "-off.png",
|
|
}).Error; err != nil {
|
|
t.Fatalf("create picture %d: %v", badgeID, err)
|
|
}
|
|
}
|
|
backpacks := []model.UserBadgeBackpack{
|
|
{ID: 2001, UserID: userID, BadgeID: 101, ExpireType: "TEMPORARY", ExpireTime: now.Add(24 * time.Hour), UseProps: true, UpdateTime: now.Add(5 * time.Minute)},
|
|
{ID: 2002, UserID: userID, BadgeID: 102, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now.Add(4 * time.Minute)},
|
|
{ID: 2003, UserID: userID, BadgeID: 103, ExpireType: expireTypePermanent, ExpireTime: now.Add(-24 * time.Hour), UseProps: true, UpdateTime: now.Add(3 * time.Minute)},
|
|
{ID: 2004, UserID: userID, BadgeID: 104, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now.Add(2 * time.Minute)},
|
|
{ID: 2005, UserID: userID, BadgeID: 105, ExpireType: "TEMPORARY", ExpireTime: now.Add(-time.Minute), UseProps: true, UpdateTime: now.Add(time.Minute)},
|
|
{ID: 2006, UserID: userID, BadgeID: 106, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: false, UpdateTime: now},
|
|
}
|
|
if err := db.Create(&backpacks).Error; err != nil {
|
|
t.Fatalf("create backpacks: %v", err)
|
|
}
|
|
|
|
service := NewService(db)
|
|
service.now = func() time.Time { return now }
|
|
resp, err := service.Current(context.Background(), common.AuthUser{UserID: userID, SysOrigin: "likei"})
|
|
if err != nil {
|
|
t.Fatalf("Current() error = %v", err)
|
|
}
|
|
if resp.SysOrigin != "LIKEI" {
|
|
t.Fatalf("SysOrigin = %q, want LIKEI", resp.SysOrigin)
|
|
}
|
|
if len(resp.LongBadges) != 1 {
|
|
t.Fatalf("long badges len = %d, want 1: %#v", len(resp.LongBadges), resp.LongBadges)
|
|
}
|
|
if len(resp.ShortBadges) != 2 {
|
|
t.Fatalf("short badges len = %d, want 2: %#v", len(resp.ShortBadges), resp.ShortBadges)
|
|
}
|
|
if len(resp.Badges) != 3 {
|
|
t.Fatalf("badges len = %d, want 3", len(resp.Badges))
|
|
}
|
|
long := resp.LongBadges[0]
|
|
if long.Type != badgeDisplayTypeLong || long.SourceType != badgeSourceTypeActivity || long.BadgeID != "101" || long.LevelCode != "VIP2" {
|
|
t.Fatalf("long badge = %#v", long)
|
|
}
|
|
if long.Name != "VIP Badge" || long.URL != "https://cdn.example.com/101.png" {
|
|
t.Fatalf("long badge name/url = %q/%q", long.Name, long.URL)
|
|
}
|
|
if resp.ShortBadges[0].BadgeID != "102" || resp.ShortBadges[0].SourceType != badgeSourceTypeActivity {
|
|
t.Fatalf("first short badge = %#v", resp.ShortBadges[0])
|
|
}
|
|
if resp.ShortBadges[1].BadgeID != "103" || resp.ShortBadges[1].SourceType != badgeSourceTypeAchievement {
|
|
t.Fatalf("second short badge = %#v", resp.ShortBadges[1])
|
|
}
|
|
}
|
|
|
|
func TestCurrentBadgesUsesConfiguredDisplayType(t *testing.T) {
|
|
db := newUserBadgeTestDB(t)
|
|
now := time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
|
|
userID := int64(42)
|
|
seedBadgeConfigWithDisplay(t, db, 301, badgeSourceTypeActivity, badgeDisplayTypeLong, "Activity Long", "ACTIVITY_LONG", 1)
|
|
seedBadgeConfigWithDisplay(t, db, 302, badgeSourceTypeVIP, badgeDisplayTypeShort, "VIP Short", "VIP_SHORT", 1)
|
|
for _, badgeID := range []int64{301, 302} {
|
|
if err := db.Create(&model.SysBadgePictureConfig{
|
|
ID: badgeID + 1000,
|
|
BadgeConfigID: badgeID,
|
|
SysOrigin: "LIKEI",
|
|
SelectURL: "https://cdn.example.com/" + formatID(badgeID) + ".png",
|
|
}).Error; err != nil {
|
|
t.Fatalf("create picture %d: %v", badgeID, err)
|
|
}
|
|
}
|
|
backpacks := []model.UserBadgeBackpack{
|
|
{ID: 4001, UserID: userID, BadgeID: 301, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now.Add(time.Minute)},
|
|
{ID: 4002, UserID: userID, BadgeID: 302, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now},
|
|
}
|
|
if err := db.Create(&backpacks).Error; err != nil {
|
|
t.Fatalf("create backpacks: %v", err)
|
|
}
|
|
|
|
service := NewService(db)
|
|
service.now = func() time.Time { return now }
|
|
resp, err := service.Current(context.Background(), common.AuthUser{UserID: userID, SysOrigin: "LIKEI"})
|
|
if err != nil {
|
|
t.Fatalf("Current() error = %v", err)
|
|
}
|
|
if len(resp.LongBadges) != 1 || resp.LongBadges[0].BadgeID != "301" || resp.LongBadges[0].Type != badgeDisplayTypeLong {
|
|
t.Fatalf("long badges = %#v, want configured activity long badge", resp.LongBadges)
|
|
}
|
|
if len(resp.ShortBadges) != 1 || resp.ShortBadges[0].BadgeID != "302" || resp.ShortBadges[0].Type != badgeDisplayTypeShort {
|
|
t.Fatalf("short badges = %#v, want configured vip short badge", resp.ShortBadges)
|
|
}
|
|
}
|
|
|
|
func TestCurrentBadgesRejectsInvalidUser(t *testing.T) {
|
|
service := NewService(newUserBadgeTestDB(t))
|
|
_, err := service.Current(context.Background(), common.AuthUser{SysOrigin: "LIKEI"})
|
|
if err == nil {
|
|
t.Fatalf("Current() error = nil, want invalid user")
|
|
}
|
|
appErr, ok := err.(*AppError)
|
|
if !ok || appErr.Code != "invalid_user" {
|
|
t.Fatalf("error = %#v, want invalid_user", err)
|
|
}
|
|
}
|
|
|
|
func TestOtherBadgesUsesTargetUser(t *testing.T) {
|
|
db := newUserBadgeTestDB(t)
|
|
now := time.Date(2026, 5, 15, 12, 0, 0, 0, time.UTC)
|
|
viewerID := int64(42)
|
|
targetID := int64(99)
|
|
seedBadgeConfig(t, db, 201, badgeSourceTypeActivity, "Viewer Badge", "VIEWER_BADGE", 1)
|
|
seedBadgeConfig(t, db, 202, badgeSourceTypeAchievement, "Target Badge", "TARGET_BADGE", 2)
|
|
for _, badgeID := range []int64{201, 202} {
|
|
if err := db.Create(&model.SysBadgePictureConfig{
|
|
ID: badgeID + 1000,
|
|
BadgeConfigID: badgeID,
|
|
SysOrigin: "LIKEI",
|
|
SelectURL: "https://cdn.example.com/" + formatID(badgeID) + ".png",
|
|
}).Error; err != nil {
|
|
t.Fatalf("create picture %d: %v", badgeID, err)
|
|
}
|
|
}
|
|
backpacks := []model.UserBadgeBackpack{
|
|
{ID: 3001, UserID: viewerID, BadgeID: 201, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now},
|
|
{ID: 3002, UserID: targetID, BadgeID: 202, ExpireType: "TEMPORARY", ExpireTime: now.Add(2 * time.Hour), UseProps: true, UpdateTime: now.Add(time.Minute)},
|
|
}
|
|
if err := db.Create(&backpacks).Error; err != nil {
|
|
t.Fatalf("create backpacks: %v", err)
|
|
}
|
|
|
|
service := NewService(db)
|
|
service.now = func() time.Time { return now }
|
|
resp, err := service.Other(context.Background(), common.AuthUser{UserID: viewerID, SysOrigin: "LIKEI"}, targetID)
|
|
if err != nil {
|
|
t.Fatalf("Other() error = %v", err)
|
|
}
|
|
if resp.UserID != targetID {
|
|
t.Fatalf("UserID = %d, want target %d", resp.UserID, targetID)
|
|
}
|
|
if len(resp.ShortBadges) != 1 || resp.ShortBadges[0].BadgeID != "202" {
|
|
t.Fatalf("short badges = %#v, want target badge 202", resp.ShortBadges)
|
|
}
|
|
}
|
|
|
|
func TestOtherBadgesRejectsInvalidTargetUser(t *testing.T) {
|
|
service := NewService(newUserBadgeTestDB(t))
|
|
_, err := service.Other(context.Background(), common.AuthUser{UserID: 42, SysOrigin: "LIKEI"}, 0)
|
|
if err == nil {
|
|
t.Fatalf("Other() error = nil, want invalid target user")
|
|
}
|
|
appErr, ok := err.(*AppError)
|
|
if !ok || appErr.Code != "invalid_target_user_id" {
|
|
t.Fatalf("error = %#v, want invalid_target_user_id", err)
|
|
}
|
|
}
|
|
|
|
func newUserBadgeTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(
|
|
&model.UserVipState{},
|
|
&model.UserBadgeBackpack{},
|
|
&model.SysBadgeConfig{},
|
|
&model.SysBadgePictureConfig{},
|
|
); err != nil {
|
|
t.Fatalf("migrate sqlite: %v", err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func seedBadgeConfig(t *testing.T, db *gorm.DB, id int64, sourceType string, name string, key string, level int) {
|
|
t.Helper()
|
|
seedBadgeConfigWithDisplay(t, db, id, sourceType, "", name, key, level)
|
|
}
|
|
|
|
func seedBadgeConfigWithDisplay(t *testing.T, db *gorm.DB, id int64, sourceType string, displayType string, name string, key string, level int) {
|
|
t.Helper()
|
|
if err := db.Create(&model.SysBadgeConfig{
|
|
ID: id,
|
|
Type: sourceType,
|
|
DisplayType: displayType,
|
|
BadgeName: name,
|
|
BadgeKey: key,
|
|
BadgeLevel: level,
|
|
Milestone: id * 10,
|
|
Del: false,
|
|
}).Error; err != nil {
|
|
t.Fatalf("create badge config %d: %v", id, err)
|
|
}
|
|
}
|