package router import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "chatapp3-golang/internal/config" "chatapp3-golang/internal/integration" "chatapp3-golang/internal/model" "chatapp3-golang/internal/repo" "chatapp3-golang/internal/service/userbadge" "github.com/gin-gonic/gin" "gorm.io/driver/sqlite" "gorm.io/gorm" ) type userBadgeAuthStub struct{} func (userBadgeAuthStub) AuthenticateToken(context.Context, string) (integration.UserCredential, error) { return integration.UserCredential{UserID: 123, SysOrigin: "LIKEI"}, nil } func (userBadgeAuthStub) AuthenticateConsoleToken(context.Context, string) (integration.ConsoleAccount, error) { return integration.ConsoleAccount{}, nil } func TestUserBadgeCurrentRoute(t *testing.T) { gin.SetMode(gin.TestMode) db := newRouterUserBadgeDB(t) now := time.Now().Add(time.Hour) if err := db.Create(&model.UserBadgeBackpack{ ID: 1, UserID: 123, BadgeID: 501, ExpireType: "TEMPORARY", ExpireTime: now, UseProps: true, UpdateTime: time.Now(), }).Error; err != nil { t.Fatalf("create backpack: %v", err) } if err := db.Create(&model.SysBadgeConfig{ ID: 501, Type: "ACTIVITY", BadgeName: "Activity Badge", BadgeKey: "ACTIVITY_BADGE", }).Error; err != nil { t.Fatalf("create badge config: %v", err) } if err := db.Create(&model.SysBadgePictureConfig{ ID: 601, BadgeConfigID: 501, SysOrigin: "LIKEI", SelectURL: "https://cdn.example.com/activity.png", }).Error; err != nil { t.Fatalf("create badge picture: %v", err) } service := userbadge.NewService(db) engine := NewRouter(config.Config{}, &repo.Repository{}, userBadgeAuthStub{}, Services{UserBadge: service}) req := httptest.NewRequest(http.MethodGet, "/app/user/badge/current", nil) req.Header.Set("Authorization", "Bearer token") rec := httptest.NewRecorder() engine.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body = %s", rec.Code, rec.Body.String()) } var payload map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode response: %v", err) } data, ok := payload["data"].(map[string]any) if !ok { t.Fatalf("data missing: %#v", payload) } shortBadges, ok := data["shortBadges"].([]any) if !ok || len(shortBadges) != 1 { t.Fatalf("shortBadges = %#v", data["shortBadges"]) } item, ok := shortBadges[0].(map[string]any) if !ok || item["type"] != "SHORT" || item["badgeId"] != "501" { t.Fatalf("short badge item = %#v", shortBadges[0]) } } func TestUserBadgeCurrentRouteRequiresAuth(t *testing.T) { gin.SetMode(gin.TestMode) service := userbadge.NewService(newRouterUserBadgeDB(t)) engine := NewRouter(config.Config{}, &repo.Repository{}, userBadgeAuthStub{}, Services{UserBadge: service}) req := httptest.NewRequest(http.MethodGet, "/app/user/badge/current", nil) rec := httptest.NewRecorder() engine.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized { t.Fatalf("status = %d body = %s, want 401", rec.Code, rec.Body.String()) } } func TestUserBadgeOtherRoute(t *testing.T) { gin.SetMode(gin.TestMode) db := newRouterUserBadgeDB(t) targetUserID := int64(456) if err := db.Create(&model.UserBadgeBackpack{ ID: 2, UserID: targetUserID, BadgeID: 502, ExpireType: "TEMPORARY", ExpireTime: time.Now().Add(time.Hour), UseProps: true, UpdateTime: time.Now(), }).Error; err != nil { t.Fatalf("create backpack: %v", err) } if err := db.Create(&model.SysBadgeConfig{ ID: 502, Type: "ACHIEVEMENT", BadgeName: "Achievement Badge", BadgeKey: "ACHIEVEMENT_BADGE", }).Error; err != nil { t.Fatalf("create badge config: %v", err) } if err := db.Create(&model.SysBadgePictureConfig{ ID: 602, BadgeConfigID: 502, SysOrigin: "LIKEI", SelectURL: "https://cdn.example.com/achievement.png", }).Error; err != nil { t.Fatalf("create badge picture: %v", err) } service := userbadge.NewService(db) engine := NewRouter(config.Config{}, &repo.Repository{}, userBadgeAuthStub{}, Services{UserBadge: service}) req := httptest.NewRequest(http.MethodGet, "/app/user/badge/other?userId=456", nil) req.Header.Set("Authorization", "Bearer token") rec := httptest.NewRecorder() engine.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body = %s", rec.Code, rec.Body.String()) } var payload map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode response: %v", err) } data, ok := payload["data"].(map[string]any) if !ok { t.Fatalf("data missing: %#v", payload) } if data["userId"] != float64(targetUserID) { t.Fatalf("userId = %#v, want target user", data["userId"]) } shortBadges, ok := data["shortBadges"].([]any) if !ok || len(shortBadges) != 1 { t.Fatalf("shortBadges = %#v", data["shortBadges"]) } item, ok := shortBadges[0].(map[string]any) if !ok || item["sourceType"] != "ACHIEVEMENT" || item["badgeId"] != "502" { t.Fatalf("short badge item = %#v", shortBadges[0]) } } func TestUserBadgeOtherRouteRequiresTargetUser(t *testing.T) { gin.SetMode(gin.TestMode) service := userbadge.NewService(newRouterUserBadgeDB(t)) engine := NewRouter(config.Config{}, &repo.Repository{}, userBadgeAuthStub{}, Services{UserBadge: service}) req := httptest.NewRequest(http.MethodGet, "/app/user/badge/other", nil) req.Header.Set("Authorization", "Bearer token") rec := httptest.NewRecorder() engine.ServeHTTP(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("status = %d body = %s, want 400", rec.Code, rec.Body.String()) } } func newRouterUserBadgeDB(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 }