2025-11-28 23:30:49 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-02 19:43:39 +08:00
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
"time"
|
2025-12-02 19:43:39 +08:00
|
|
|
|
|
|
|
|
|
|
"carrotskin/pkg/storage"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// TestUploadService_FileTypes 测试文件类型常量
|
|
|
|
|
|
func TestUploadService_FileTypes(t *testing.T) {
|
|
|
|
|
|
if FileTypeAvatar == "" {
|
|
|
|
|
|
t.Error("FileTypeAvatar should not be empty")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if FileTypeTexture == "" {
|
|
|
|
|
|
t.Error("FileTypeTexture should not be empty")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if FileTypeAvatar == FileTypeTexture {
|
|
|
|
|
|
t.Error("FileTypeAvatar and FileTypeTexture should be different")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestGetUploadConfig 测试获取上传配置
|
|
|
|
|
|
func TestGetUploadConfig(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
fileType FileType
|
|
|
|
|
|
wantConfig bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "头像类型返回配置",
|
|
|
|
|
|
fileType: FileTypeAvatar,
|
|
|
|
|
|
wantConfig: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "材质类型返回配置",
|
|
|
|
|
|
fileType: FileTypeTexture,
|
|
|
|
|
|
wantConfig: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "无效类型返回nil",
|
|
|
|
|
|
fileType: FileType("invalid"),
|
|
|
|
|
|
wantConfig: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
config := GetUploadConfig(tt.fileType)
|
|
|
|
|
|
hasConfig := config != nil
|
|
|
|
|
|
if hasConfig != tt.wantConfig {
|
|
|
|
|
|
t.Errorf("GetUploadConfig() = %v, want %v", hasConfig, tt.wantConfig)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config != nil {
|
|
|
|
|
|
// 验证配置字段
|
|
|
|
|
|
if config.MinSize <= 0 {
|
|
|
|
|
|
t.Error("MinSize should be greater than 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
if config.MaxSize <= 0 {
|
|
|
|
|
|
t.Error("MaxSize should be greater than 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
if config.MaxSize < config.MinSize {
|
|
|
|
|
|
t.Error("MaxSize should be greater than or equal to MinSize")
|
|
|
|
|
|
}
|
|
|
|
|
|
if config.Expires <= 0 {
|
|
|
|
|
|
t.Error("Expires should be greater than 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(config.AllowedExts) == 0 {
|
|
|
|
|
|
t.Error("AllowedExts should not be empty")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestGetUploadConfig_AvatarConfig 测试头像配置详情
|
|
|
|
|
|
func TestGetUploadConfig_AvatarConfig(t *testing.T) {
|
|
|
|
|
|
config := GetUploadConfig(FileTypeAvatar)
|
|
|
|
|
|
if config == nil {
|
|
|
|
|
|
t.Fatal("Avatar config should not be nil")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证允许的扩展名
|
|
|
|
|
|
expectedExts := []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
|
|
|
|
|
for _, ext := range expectedExts {
|
|
|
|
|
|
if !config.AllowedExts[ext] {
|
|
|
|
|
|
t.Errorf("Avatar config should allow %s extension", ext)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证文件大小限制
|
|
|
|
|
|
if config.MinSize != 1024 {
|
|
|
|
|
|
t.Errorf("Avatar MinSize = %d, want 1024", config.MinSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.MaxSize != 5*1024*1024 {
|
|
|
|
|
|
t.Errorf("Avatar MaxSize = %d, want 5MB", config.MaxSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证过期时间
|
|
|
|
|
|
if config.Expires != 15*time.Minute {
|
|
|
|
|
|
t.Errorf("Avatar Expires = %v, want 15 minutes", config.Expires)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestGetUploadConfig_TextureConfig 测试材质配置详情
|
|
|
|
|
|
func TestGetUploadConfig_TextureConfig(t *testing.T) {
|
|
|
|
|
|
config := GetUploadConfig(FileTypeTexture)
|
|
|
|
|
|
if config == nil {
|
|
|
|
|
|
t.Fatal("Texture config should not be nil")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证允许的扩展名(材质只允许PNG)
|
|
|
|
|
|
if !config.AllowedExts[".png"] {
|
|
|
|
|
|
t.Error("Texture config should allow .png extension")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证文件大小限制
|
|
|
|
|
|
if config.MinSize != 1024 {
|
|
|
|
|
|
t.Errorf("Texture MinSize = %d, want 1024", config.MinSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.MaxSize != 10*1024*1024 {
|
|
|
|
|
|
t.Errorf("Texture MaxSize = %d, want 10MB", config.MaxSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证过期时间
|
|
|
|
|
|
if config.Expires != 15*time.Minute {
|
|
|
|
|
|
t.Errorf("Texture Expires = %v, want 15 minutes", config.Expires)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestValidateFileName 测试文件名验证
|
|
|
|
|
|
func TestValidateFileName(t *testing.T) {
|
|
|
|
|
|
tests := []struct {
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name string
|
|
|
|
|
|
fileName string
|
|
|
|
|
|
fileType FileType
|
|
|
|
|
|
wantErr bool
|
2025-11-28 23:30:49 +08:00
|
|
|
|
errContains string
|
|
|
|
|
|
}{
|
|
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name: "有效的头像文件名",
|
|
|
|
|
|
fileName: "avatar.png",
|
|
|
|
|
|
fileType: FileTypeAvatar,
|
|
|
|
|
|
wantErr: false,
|
2025-11-28 23:30:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name: "有效的材质文件名",
|
|
|
|
|
|
fileName: "texture.png",
|
|
|
|
|
|
fileType: FileTypeTexture,
|
|
|
|
|
|
wantErr: false,
|
2025-11-28 23:30:49 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name: "文件名为空",
|
|
|
|
|
|
fileName: "",
|
|
|
|
|
|
fileType: FileTypeAvatar,
|
|
|
|
|
|
wantErr: true,
|
2025-11-28 23:30:49 +08:00
|
|
|
|
errContains: "文件名不能为空",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name: "不支持的文件扩展名",
|
|
|
|
|
|
fileName: "file.txt",
|
|
|
|
|
|
fileType: FileTypeAvatar,
|
|
|
|
|
|
wantErr: true,
|
2025-11-28 23:30:49 +08:00
|
|
|
|
errContains: "不支持的文件格式",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-12-02 19:43:39 +08:00
|
|
|
|
name: "无效的文件类型",
|
|
|
|
|
|
fileName: "file.png",
|
|
|
|
|
|
fileType: FileType("invalid"),
|
|
|
|
|
|
wantErr: true,
|
2025-11-28 23:30:49 +08:00
|
|
|
|
errContains: "不支持的文件类型",
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
err := ValidateFileName(tt.fileName, tt.fileType)
|
|
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
|
|
t.Errorf("ValidateFileName() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if tt.wantErr && tt.errContains != "" {
|
|
|
|
|
|
if err == nil || !strings.Contains(err.Error(), tt.errContains) {
|
|
|
|
|
|
t.Errorf("ValidateFileName() error = %v, should contain %s", err, tt.errContains)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestValidateFileName_Extensions 测试各种扩展名
|
|
|
|
|
|
func TestValidateFileName_Extensions(t *testing.T) {
|
|
|
|
|
|
avatarExts := []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
|
|
|
|
|
for _, ext := range avatarExts {
|
|
|
|
|
|
fileName := "test" + ext
|
|
|
|
|
|
err := ValidateFileName(fileName, FileTypeAvatar)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Errorf("Avatar file with %s extension should be valid, got error: %v", ext, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 材质只支持PNG
|
|
|
|
|
|
textureExts := []string{".png"}
|
|
|
|
|
|
for _, ext := range textureExts {
|
|
|
|
|
|
fileName := "test" + ext
|
|
|
|
|
|
err := ValidateFileName(fileName, FileTypeTexture)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Errorf("Texture file with %s extension should be valid, got error: %v", ext, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 测试不支持的扩展名
|
|
|
|
|
|
invalidExts := []string{".txt", ".pdf", ".doc"}
|
|
|
|
|
|
for _, ext := range invalidExts {
|
|
|
|
|
|
fileName := "test" + ext
|
|
|
|
|
|
err := ValidateFileName(fileName, FileTypeAvatar)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Errorf("Avatar file with %s extension should be invalid", ext)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestValidateFileName_CaseInsensitive 测试扩展名大小写不敏感
|
|
|
|
|
|
func TestValidateFileName_CaseInsensitive(t *testing.T) {
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
|
|
fileName string
|
|
|
|
|
|
fileType FileType
|
|
|
|
|
|
wantErr bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"test.PNG", FileTypeAvatar, false},
|
|
|
|
|
|
{"test.JPG", FileTypeAvatar, false},
|
|
|
|
|
|
{"test.JPEG", FileTypeAvatar, false},
|
|
|
|
|
|
{"test.GIF", FileTypeAvatar, false},
|
|
|
|
|
|
{"test.WEBP", FileTypeAvatar, false},
|
|
|
|
|
|
{"test.PnG", FileTypeTexture, false},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
|
|
t.Run(tc.fileName, func(t *testing.T) {
|
|
|
|
|
|
err := ValidateFileName(tc.fileName, tc.fileType)
|
|
|
|
|
|
if (err != nil) != tc.wantErr {
|
|
|
|
|
|
t.Errorf("ValidateFileName(%s, %s) error = %v, wantErr %v", tc.fileName, tc.fileType, err, tc.wantErr)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestUploadConfig_Structure 测试UploadConfig结构
|
|
|
|
|
|
func TestUploadConfig_Structure(t *testing.T) {
|
|
|
|
|
|
config := &UploadConfig{
|
|
|
|
|
|
AllowedExts: map[string]bool{
|
|
|
|
|
|
".png": true,
|
|
|
|
|
|
},
|
|
|
|
|
|
MinSize: 1024,
|
|
|
|
|
|
MaxSize: 5 * 1024 * 1024,
|
|
|
|
|
|
Expires: 15 * time.Minute,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.AllowedExts == nil {
|
|
|
|
|
|
t.Error("AllowedExts should not be nil")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.MinSize <= 0 {
|
|
|
|
|
|
t.Error("MinSize should be greater than 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.MaxSize <= config.MinSize {
|
|
|
|
|
|
t.Error("MaxSize should be greater than MinSize")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if config.Expires <= 0 {
|
|
|
|
|
|
t.Error("Expires should be greater than 0")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 19:43:39 +08:00
|
|
|
|
// mockStorageClient 用于单元测试的简单存储客户端假实现
|
|
|
|
|
|
// 注意:这里只声明与 upload_service 使用到的方法,避免依赖真实 MinIO 客户端
|
|
|
|
|
|
type mockStorageClient struct {
|
|
|
|
|
|
getBucketFn func(name string) (string, error)
|
|
|
|
|
|
generatePresignedPostURLFn func(ctx context.Context, bucketName, objectName string, minSize, maxSize int64, expires time.Duration) (*storage.PresignedPostPolicyResult, error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (m *mockStorageClient) GetBucket(name string) (string, error) {
|
|
|
|
|
|
if m.getBucketFn != nil {
|
|
|
|
|
|
return m.getBucketFn(name)
|
|
|
|
|
|
}
|
|
|
|
|
|
return "", errors.New("GetBucket not implemented")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (m *mockStorageClient) GeneratePresignedPostURL(ctx context.Context, bucketName, objectName string, minSize, maxSize int64, expires time.Duration) (*storage.PresignedPostPolicyResult, error) {
|
|
|
|
|
|
if m.generatePresignedPostURLFn != nil {
|
|
|
|
|
|
return m.generatePresignedPostURLFn(ctx, bucketName, objectName, minSize, maxSize, expires)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil, errors.New("GeneratePresignedPostURL not implemented")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestGenerateAvatarUploadURL_Success 测试头像上传URL生成成功
|
|
|
|
|
|
func TestGenerateAvatarUploadURL_Success(t *testing.T) {
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
mockClient := &mockStorageClient{
|
|
|
|
|
|
getBucketFn: func(name string) (string, error) {
|
|
|
|
|
|
if name != "avatars" {
|
|
|
|
|
|
t.Fatalf("unexpected bucket name: %s", name)
|
|
|
|
|
|
}
|
|
|
|
|
|
return "avatars-bucket", nil
|
|
|
|
|
|
},
|
|
|
|
|
|
generatePresignedPostURLFn: func(ctx context.Context, bucketName, objectName string, minSize, maxSize int64, expires time.Duration) (*storage.PresignedPostPolicyResult, error) {
|
|
|
|
|
|
if bucketName != "avatars-bucket" {
|
|
|
|
|
|
t.Fatalf("unexpected bucketName: %s", bucketName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.Contains(objectName, "user_") {
|
|
|
|
|
|
t.Fatalf("objectName should contain user_ prefix, got: %s", objectName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.Contains(objectName, "avatar.png") {
|
|
|
|
|
|
t.Fatalf("objectName should contain original file name, got: %s", objectName)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 检查大小与过期时间传递
|
|
|
|
|
|
if minSize != 1024 {
|
|
|
|
|
|
t.Fatalf("minSize = %d, want 1024", minSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
if maxSize != 5*1024*1024 {
|
|
|
|
|
|
t.Fatalf("maxSize = %d, want 5MB", maxSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
if expires != 15*time.Minute {
|
|
|
|
|
|
t.Fatalf("expires = %v, want 15m", expires)
|
|
|
|
|
|
}
|
|
|
|
|
|
return &storage.PresignedPostPolicyResult{
|
|
|
|
|
|
PostURL: "http://example.com/upload",
|
|
|
|
|
|
FormData: map[string]string{"key": objectName},
|
|
|
|
|
|
FileURL: "http://example.com/file/" + objectName,
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接将 mock 实例转换为真实类型使用(依赖其方法集与被测代码一致)
|
|
|
|
|
|
storageClient := (*storage.StorageClient)(nil)
|
|
|
|
|
|
_ = storageClient // 避免未使用告警,实际调用仍通过 mockClient 完成
|
|
|
|
|
|
|
|
|
|
|
|
// 直接通过内部使用接口的实现进行测试,避免依赖真实 StorageClient
|
|
|
|
|
|
result, err := generateAvatarUploadURLWithClient(ctx, mockClient, 123, "avatar.png")
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("GenerateAvatarUploadURL() error = %v, want nil", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if result == nil {
|
|
|
|
|
|
t.Fatalf("GenerateAvatarUploadURL() result is nil")
|
|
|
|
|
|
}
|
|
|
|
|
|
if result.PostURL == "" || result.FileURL == "" {
|
|
|
|
|
|
t.Fatalf("GenerateAvatarUploadURL() result has empty URLs: %+v", result)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestGenerateTextureUploadURL_Success 测试材质上传URL生成成功(SKIN/CAPE)
|
|
|
|
|
|
func TestGenerateTextureUploadURL_Success(t *testing.T) {
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
textureType string
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"SKIN 材质", "SKIN"},
|
|
|
|
|
|
{"CAPE 材质", "CAPE"},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
|
mockClient := &mockStorageClient{
|
|
|
|
|
|
getBucketFn: func(name string) (string, error) {
|
|
|
|
|
|
if name != "textures" {
|
|
|
|
|
|
t.Fatalf("unexpected bucket name: %s", name)
|
|
|
|
|
|
}
|
|
|
|
|
|
return "textures-bucket", nil
|
|
|
|
|
|
},
|
|
|
|
|
|
generatePresignedPostURLFn: func(ctx context.Context, bucketName, objectName string, minSize, maxSize int64, expires time.Duration) (*storage.PresignedPostPolicyResult, error) {
|
|
|
|
|
|
if bucketName != "textures-bucket" {
|
|
|
|
|
|
t.Fatalf("unexpected bucketName: %s", bucketName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.Contains(objectName, "texture.png") {
|
|
|
|
|
|
t.Fatalf("objectName should contain original file name, got: %s", objectName)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.Contains(objectName, "/"+strings.ToLower(tt.textureType)+"/") {
|
|
|
|
|
|
t.Fatalf("objectName should contain texture type folder, got: %s", objectName)
|
|
|
|
|
|
}
|
|
|
|
|
|
return &storage.PresignedPostPolicyResult{
|
|
|
|
|
|
PostURL: "http://example.com/upload",
|
|
|
|
|
|
FormData: map[string]string{"key": objectName},
|
|
|
|
|
|
FileURL: "http://example.com/file/" + objectName,
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result, err := generateTextureUploadURLWithClient(ctx, mockClient, 123, "texture.png", tt.textureType)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("generateTextureUploadURLWithClient() error = %v, want nil", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if result == nil || result.PostURL == "" || result.FileURL == "" {
|
|
|
|
|
|
t.Fatalf("generateTextureUploadURLWithClient() result invalid: %+v", result)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|