package gamemanagement import ( "context" "encoding/base64" "io" "net/http" "net/http/httptest" "strings" "testing" "unicode" ) type fakeRobotAvatarUploader struct { key string contentType string data []byte } func (u *fakeRobotAvatarUploader) PutObject(_ context.Context, key string, reader io.Reader, _ int64, contentType string) (string, error) { data, err := io.ReadAll(reader) if err != nil { return "", err } u.key = key u.contentType = contentType u.data = data return "https://media.example.com/" + key, nil } func TestUploadRobotAvatarFromSourceUploadsDownloadedImageToCOS(t *testing.T) { imageBytes, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=") if err != nil { t.Fatalf("decode png: %v", err) } avatarServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "image/png") _, _ = w.Write(imageBytes) })) defer avatarServer.Close() uploader := &fakeRobotAvatarUploader{} handler := New(nil, nil, nil, WithRobotAvatarUploader(uploader, "admin"), WithRobotAvatarHTTPClient(avatarServer.Client())) got, err := handler.uploadRobotAvatarFromSource(t.Context(), 42, avatarServer.URL+"/avatar.png") if err != nil { t.Fatalf("uploadRobotAvatarFromSource failed: %v", err) } if !strings.HasPrefix(got, "https://media.example.com/admin/game-robots/avatars/42/") { t.Fatalf("uploaded url = %q", got) } if uploader.contentType != "image/png" { t.Fatalf("contentType = %q, want image/png", uploader.contentType) } if string(uploader.data) != string(imageBytes) { t.Fatalf("uploaded bytes mismatch") } } func TestRobotNicknameUsesSingleLanguagePresetWithoutDigits(t *testing.T) { // 预设池是批量创建机器人的基础数据,数量变化会直接影响前端一次创建 200 个机器人时的去重效果。 if len(defaultArabicRobotNicknames) != 100 { t.Fatalf("arabic nickname preset count = %d, want 100", len(defaultArabicRobotNicknames)) } if len(defaultEnglishRobotNicknames) != 100 { t.Fatalf("english nickname preset count = %d, want 100", len(defaultEnglishRobotNicknames)) } if len(defaultRobotAvatarSources) != 200 { t.Fatalf("avatar preset count = %d, want 200", len(defaultRobotAvatarSources)) } if uniqueCount(defaultRobotAvatarSources) != 200 { t.Fatalf("avatar preset must keep 200 unique source urls") } englishNames := make([]string, 0, 200) arabicNames := make([]string, 0, 200) for index := 0; index < 200; index++ { english := robotNickname(index, "english") if !isPureEnglishNickname(english) { t.Fatalf("english nickname[%d] = %q, want letters only", index, english) } englishNames = append(englishNames, english) arabic := robotNickname(index, "arabic") if !isPureArabicNickname(arabic) { t.Fatalf("arabic nickname[%d] = %q, want arabic letters only", index, arabic) } arabicNames = append(arabicNames, arabic) } if uniqueCount(englishNames) != len(englishNames) { t.Fatalf("english nickname presets and overflow combinations must keep 200 unique names") } if uniqueCount(arabicNames) != len(arabicNames) { t.Fatalf("arabic nickname presets and overflow combinations must keep 200 unique names") } if got := robotNickname(100, "english"); got != "AlexJordan" { t.Fatalf("english overflow nickname = %q, want pure english combined name", got) } if got := robotNickname(100, "arabic"); got != "أميرسيف" { t.Fatalf("arabic overflow nickname = %q, want pure arabic combined name", got) } } func isPureEnglishNickname(value string) bool { if value == "" { return false } for _, item := range value { if (item < 'A' || item > 'Z') && (item < 'a' || item > 'z') { return false } } return true } func isPureArabicNickname(value string) bool { if value == "" { return false } for _, item := range value { if !unicode.Is(unicode.Arabic, item) { return false } } return true } func uniqueCount(values []string) int { seen := make(map[string]struct{}, len(values)) for _, value := range values { seen[value] = struct{}{} } return len(seen) }