package resource import ( "encoding/json" "strconv" "strings" "testing" ) func TestParseGrantTargetUserIDAcceptsStringLongID(t *testing.T) { userID, err := parseGrantTargetUserID("318705991371722752") if err != nil { t.Fatalf("string long user id should be accepted: %v", err) } if userID != 318705991371722752 { t.Fatalf("user id mismatch: %d", userID) } } func TestParseGrantTargetUserIDKeepsLegacyNumber(t *testing.T) { userID, err := parseGrantTargetUserID(float64(163000)) if err != nil { t.Fatalf("legacy numeric user id should be accepted: %v", err) } if userID != 163000 { t.Fatalf("user id mismatch: %d", userID) } } func TestApplyGrantOperatorsUsesSourceSpecificProfiles(t *testing.T) { grants := []grantDTO{ {GrantID: "grant-admin", GrantSource: "admin", OperatorUserID: 7}, {GrantID: "grant-manager", GrantSource: "manager_center", OperatorUserID: 9001}, } applyGrantOperators(grants, map[uint]adminOperator{ 7: {UserID: 7, Username: "admin", Name: "后台管理员"}, }, map[int64]managerOperator{ 9001: {UserID: 9001, DisplayUserID: "10086", Username: "经理 A", Avatar: "https://cdn.example/avatar.png"}, }) if grants[0].Operator == nil || grants[0].Operator.Source != "admin" || grants[0].Operator.Name != "后台管理员" || grants[0].Operator.Username != "admin" { t.Fatalf("admin operator mismatch: %+v", grants[0].Operator) } if grants[1].Operator == nil || grants[1].Operator.Source != "manager_center" || grants[1].Operator.DisplayUserID != "10086" || grants[1].Operator.Avatar == "" || grants[1].Operator.Username != "经理 A" { t.Fatalf("manager operator mismatch: %+v", grants[1].Operator) } } func TestApplyGrantTargetUsersKeepsFallbackUserID(t *testing.T) { grants := []grantDTO{ {GrantID: "grant-with-user", TargetUserID: 1001}, {GrantID: "grant-missing-user", TargetUserID: 1002}, } applyGrantTargetUsers(grants, map[int64]grantUserDTO{ 1001: {UserID: 1001, DisplayUserID: "900001", Username: "玩家 A", Avatar: "https://cdn.example/user.png"}, }) if grants[0].TargetUser == nil || grants[0].TargetUser.DisplayUserID != "900001" || grants[0].TargetUser.Username != "玩家 A" { t.Fatalf("target user mismatch: %+v", grants[0].TargetUser) } if grants[1].TargetUser == nil || grants[1].TargetUser.UserID != 1002 || grants[1].TargetUser.DisplayUserID != "" { t.Fatalf("target fallback mismatch: %+v", grants[1].TargetUser) } } func TestEmojiPackMetadataDefaultsToAllRegions(t *testing.T) { metadata, err := emojiPackMetadataJSON([]int64{0, 1001}, "", "") if err != nil { t.Fatalf("metadata build failed: %v", err) } var payload emojiPackMetadataPayload if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("metadata json mismatch: %v", err) } if payload.RegionScope != "all" || len(payload.RegionIDs) != 0 || payload.Category != defaultEmojiPackCategory || payload.PricingType != emojiPackPricingFree { t.Fatalf("unexpected all-region metadata: %+v", payload) } } func TestEmojiPackMetadataKeepsLimitedRegions(t *testing.T) { metadata, err := emojiPackMetadataJSON([]int64{1001, 1001, 1002}, "会员", "paid") if err != nil { t.Fatalf("metadata build failed: %v", err) } payload := emojiPackMetadataFromJSON(metadata) if payload.RegionScope != "limited" || payload.Category != "会员" || payload.PricingType != emojiPackPricingPaid { t.Fatalf("unexpected limited-region metadata: %+v", payload) } if len(payload.RegionIDs) != 2 || payload.RegionIDs[0] != 1001 || payload.RegionIDs[1] != 1002 { t.Fatalf("unexpected region ids: %+v", payload.RegionIDs) } } func TestBadgeResourceMetadataUsesSelectedForm(t *testing.T) { metadata := resourceMetadataJSON(resourceTypeBadge, "long", "", "", "") payload := badgeMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("badge metadata json mismatch: %v", err) } if payload.BadgeForm != badgeFormStrip || payload.BadgeKind != badgeKindNormal || payload.DefaultSlot != "profile_strip" { t.Fatalf("long badge metadata mismatch: %+v", payload) } if got := badgeFormFromMetadata(resourceMetadataJSON(resourceTypeBadge, "short", "", "", "")); got != badgeFormTile { t.Fatalf("short badge form mismatch: %s", got) } } func TestBadgeResourceMetadataMarksLevelBadge(t *testing.T) { metadata := resourceMetadataJSON(resourceTypeBadge, badgeFormStrip, "", badgeKindLevel, "wealth") payload := badgeMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("badge metadata json mismatch: %v", err) } if payload.BadgeKind != badgeKindLevel || payload.LevelTrack != "wealth" || payload.BadgeForm != badgeFormStrip { t.Fatalf("level badge metadata mismatch: %+v", payload) } } func TestProfileCardResourceMetadataKeepsLayoutOnly(t *testing.T) { metadata := resourceMetadataJSON(resourceTypeProfileCard, "", `{ "profile_card_layout": { "source_width": 1136, "source_height": 1680, "color_content_width": 750, "content_top": 117, "content_bottom": 1666, "content_height": 1550, "content_top_ratio": 0, "content_height_ratio": 0, "detect_version": 99 }, "debug": true }`, "", "") payload := profileCardMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("profile card metadata json mismatch: %v", err) } if payload.ProfileCardLayout == nil { t.Fatalf("profile card layout should be kept") } if payload.ProfileCardLayout.ContentTop != 117 || payload.ProfileCardLayout.ContentHeight != 1550 { t.Fatalf("profile card layout mismatch: %+v", payload.ProfileCardLayout) } if payload.ProfileCardLayout.DetectVersion != 1 { t.Fatalf("profile card detect version should be normalized: %+v", payload.ProfileCardLayout) } if metadata == "{}" || strings.Contains(metadata, "debug") { t.Fatalf("profile card metadata should keep only layout: %s", metadata) } } func TestResourceMetadataKeepsMP4AlphaLayoutForAnimatedResources(t *testing.T) { for _, resourceType := range []string{"gift", "vehicle", "avatar_frame", "chat_bubble", "floating_screen", "mic_seat_animation"} { metadata := resourceMetadataJSON(resourceType, "", mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", true), "", "") payload := resourceMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("%s mp4 metadata json mismatch: %v", resourceType, err) } if payload.AnimationFormat != resourceAnimationFormatMP4 || payload.MP4AlphaLayout == nil { t.Fatalf("%s mp4 metadata should be kept: %s", resourceType, metadata) } if payload.MP4AlphaLayout.AlphaLayout != mp4AlphaLayoutAlphaLeftRGBRight || payload.MP4AlphaLayout.RGBFrame[0] != 750 { t.Fatalf("%s mp4 layout mismatch: %+v", resourceType, payload.MP4AlphaLayout) } } } func TestResourceMetadataKeepsNormalMP4Layout(t *testing.T) { metadata := resourceMetadataJSON("gift", "", `{ "animation_format": "mp4", "mp4_alpha_layout": { "alpha_layout": "normal", "video_w": 750, "video_h": 1334, "rgb_frame": [1, 2, 3, 4], "alpha_frame": null, "confirmed": true, "detect_version": 1 } }`, "", "") payload := resourceMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("normal mp4 metadata json mismatch: %v", err) } if payload.MP4AlphaLayout == nil || payload.MP4AlphaLayout.AlphaLayout != mp4AlphaLayoutNormal { t.Fatalf("normal mp4 metadata should be kept: %s", metadata) } if payload.MP4AlphaLayout.AlphaFrame != nil || payload.MP4AlphaLayout.RGBFrame[2] != 750 || payload.MP4AlphaLayout.RGBFrame[3] != 1334 { t.Fatalf("normal mp4 layout should be normalized to full frame: %+v", payload.MP4AlphaLayout) } } func TestProfileCardResourceMetadataKeepsLayoutAndMP4AlphaLayout(t *testing.T) { metadata := resourceMetadataJSON(resourceTypeProfileCard, "", `{ "profile_card_layout": { "source_width": 1136, "source_height": 1680, "color_content_width": 750, "content_top": 117, "content_bottom": 1666, "content_height": 1550, "content_top_ratio": 0, "content_height_ratio": 0, "detect_version": 99 }, "mp4_alpha_layout": { "alpha_layout": "rgb_left_alpha_right", "video_w": 1472, "video_h": 1328, "rgb_frame": [0, 0, 736, 1328], "alpha_frame": [736, 0, 736, 1328], "confirmed": true, "detect_version": 1 } }`, "", "") payload := resourceMetadataPayload{} if err := json.Unmarshal([]byte(metadata), &payload); err != nil { t.Fatalf("profile card mp4 metadata json mismatch: %v", err) } if payload.ProfileCardLayout == nil || payload.ProfileCardLayout.ContentTop != 117 { t.Fatalf("profile card layout should be kept: %+v", payload.ProfileCardLayout) } if payload.MP4AlphaLayout == nil || payload.MP4AlphaLayout.AlphaLayout != mp4AlphaLayoutRGBLeftAlphaRight { t.Fatalf("profile card mp4 layout should be kept: %+v", payload.MP4AlphaLayout) } } func TestValidateResourceMetadataRejectsInvalidMP4AlphaLayout(t *testing.T) { cases := []struct { name string req resourceRequest }{ { name: "unconfirmed", req: resourceRequest{ ResourceType: "gift", MetadataJSON: mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", false), }, }, { name: "bad rect", req: resourceRequest{ ResourceType: "gift", MetadataJSON: `{ "mp4_alpha_layout": { "alpha_layout": "alpha_left_rgb_right", "video_w": 1500, "video_h": 1334, "rgb_frame": [750, 0, 800, 1334], "alpha_frame": [0, 0, 750, 1334], "confirmed": true, "detect_version": 1 } }`, }, }, { name: "unsupported type", req: resourceRequest{ ResourceType: resourceTypeBadge, MetadataJSON: mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", true), }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if err := validateResourceMetadata(tc.req); err == nil { t.Fatalf("invalid mp4 metadata should be rejected") } }) } if err := validateResourceMetadata(resourceRequest{ResourceType: "gift", MetadataJSON: mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", true)}); err != nil { t.Fatalf("valid mp4 metadata rejected: %v", err) } } func TestProfileCardResourceMetadataRejectsInvalidLayout(t *testing.T) { metadata := resourceMetadataJSON(resourceTypeProfileCard, "", `{ "profile_card_layout": { "source_width": 1136, "source_height": 1680, "content_top": 117, "content_bottom": 1666, "content_height": 1 } }`, "", "") if metadata != "{}" { t.Fatalf("invalid profile card layout should be dropped: %s", metadata) } } func TestValidateBadgeResourceRequiresBadgeForm(t *testing.T) { if err := validateResourceBadgeForm(resourceRequest{ResourceType: resourceTypeBadge}); err == nil { t.Fatalf("badge form should be required") } if err := validateResourceBadgeForm(resourceRequest{ResourceType: resourceTypeBadge, BadgeForm: badgeFormTile}); err != nil { t.Fatalf("valid badge form rejected: %v", err) } if err := validateResourceBadgeForm(resourceRequest{ResourceType: resourceTypeBadge, BadgeForm: badgeFormStrip, BadgeKind: badgeKindLevel}); err == nil { t.Fatalf("level badge should require level track") } if err := validateResourceBadgeForm(resourceRequest{ResourceType: resourceTypeBadge, BadgeForm: badgeFormStrip, BadgeKind: badgeKindLevel, LevelTrack: "wealth"}); err != nil { t.Fatalf("valid level badge rejected: %v", err) } if err := validateResourceBadgeForm(resourceRequest{ResourceType: "gift", BadgeForm: ""}); err != nil { t.Fatalf("non-badge resource should not require badge form: %v", err) } } func mp4AlphaLayoutMetadataJSON(alphaLayout string, confirmed bool) string { return `{ "animation_format": "mp4", "mp4_alpha_layout": { "alpha_layout": "` + alphaLayout + `", "video_w": 1500, "video_h": 1334, "rgb_frame": [750, 0, 750, 1334], "alpha_frame": [0, 0, 750, 1334], "confirmed": ` + strconv.FormatBool(confirmed) + `, "detect_version": 1 }, "debug": true }` }