package services import ( "bytes" "encoding/base64" "mime/multipart" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "platform/pkg/env" ) func TestArticleUploadImageSuccess(t *testing.T) { restore := snapshotUploadEnv() defer restore() env.UploadDir = t.TempDir() env.ArticleUploadMaxBytes = 5 * 1024 * 1024 fileHeader := newMultipartFileHeader(t, "file", "pixel.png", mustDecodeBase64(t, onePixelPNGBase64)) result, err := Article.UploadImage(fileHeader, "https://example.com") if err != nil { t.Fatalf("UploadImage returned error: %v", err) } if result.MimeType != "image/png" { t.Fatalf("unexpected mime type: %s", result.MimeType) } if !strings.HasPrefix(result.Path, "/uploads/article/") { t.Fatalf("unexpected path: %s", result.Path) } if result.URL != "https://example.com"+result.Path { t.Fatalf("unexpected url: %s", result.URL) } if result.OriginalName != "pixel.png" { t.Fatalf("unexpected original name: %s", result.OriginalName) } savedPath := filepath.Join(env.UploadDir, filepath.FromSlash(strings.TrimPrefix(result.Path, "/uploads/"))) info, err := os.Stat(savedPath) if err != nil { t.Fatalf("saved file not found: %v", err) } if info.Size() != result.Size { t.Fatalf("unexpected saved size: got %d want %d", info.Size(), result.Size) } } func TestArticleUploadImageRejectsUnsupportedType(t *testing.T) { restore := snapshotUploadEnv() defer restore() env.UploadDir = t.TempDir() env.ArticleUploadMaxBytes = 5 * 1024 * 1024 fileHeader := newMultipartFileHeader(t, "file", "note.txt", []byte("not an image")) _, err := Article.UploadImage(fileHeader, "https://example.com") if err == nil { t.Fatal("expected error, got nil") } if !strings.Contains(err.Error(), "仅支持 JPG、PNG、WEBP、GIF 图片") { t.Fatalf("unexpected error: %v", err) } } func TestArticleUploadImageRejectsOversizeFile(t *testing.T) { restore := snapshotUploadEnv() defer restore() env.UploadDir = t.TempDir() env.ArticleUploadMaxBytes = 8 fileHeader := newMultipartFileHeader(t, "file", "large.png", bytes.Repeat([]byte("a"), 9)) _, err := Article.UploadImage(fileHeader, "https://example.com") if err == nil { t.Fatal("expected error, got nil") } if !strings.Contains(err.Error(), "图片大小不能超过") { t.Fatalf("unexpected error: %v", err) } } func newMultipartFileHeader(t *testing.T, fieldName string, fileName string, content []byte) *multipart.FileHeader { t.Helper() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile(fieldName, fileName) if err != nil { t.Fatalf("CreateFormFile failed: %v", err) } if _, err := part.Write(content); err != nil { t.Fatalf("Write content failed: %v", err) } if err := writer.Close(); err != nil { t.Fatalf("Close multipart writer failed: %v", err) } req := httptest.NewRequest(http.MethodPost, "/", &body) req.Header.Set("Content-Type", writer.FormDataContentType()) if err := req.ParseMultipartForm(int64(body.Len()) + 1024); err != nil { t.Fatalf("ParseMultipartForm failed: %v", err) } file, fileHeader, err := req.FormFile(fieldName) if err != nil { t.Fatalf("FormFile failed: %v", err) } _ = file.Close() return fileHeader } func mustDecodeBase64(t *testing.T, value string) []byte { t.Helper() data, err := base64.StdEncoding.DecodeString(value) if err != nil { t.Fatalf("DecodeString failed: %v", err) } return data } func snapshotUploadEnv() func() { uploadDir := env.UploadDir uploadPublicBaseURL := env.UploadPublicBaseURL articleUploadMaxBytes := env.ArticleUploadMaxBytes return func() { env.UploadDir = uploadDir env.UploadPublicBaseURL = uploadPublicBaseURL env.ArticleUploadMaxBytes = articleUploadMaxBytes } } const onePixelPNGBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a7KQAAAAASUVORK5CYII="