package storage import ( "bytes" "image" "image/color" "image/jpeg" "image/png" "path/filepath" "testing" ) func TestDetectContentType(t *testing.T) { tests := []struct { name string data []byte want string }{ {"JPEG", []byte{0xFF, 0xD8, 0xFF, 0xE0}, "image/jpeg"}, {"PNG", []byte{0x89, 0x50, 0x4E, 0x47}, "image/png"}, {"GIF", []byte{0x47, 0x49, 0x46, 0x38}, "image/gif"}, {"WebP", []byte{0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50}, "image/webp"}, {"BMP", []byte{0x42, 0x4D}, "image/bmp"}, {"MP4", []byte{0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70}, "video/mp4"}, {"WebM", []byte{0x1A, 0x45, 0xDF, 0xA3}, "video/webm"}, {"unknown", []byte{0x00, 0x01, 0x02}, "application/octet-stream"}, {"empty", []byte{}, "application/octet-stream"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := DetectContentType(tt.data) if got != tt.want { t.Fatalf("DetectContentType = %q, want %q", got, tt.want) } }) } } func TestExtensionFromMIME(t *testing.T) { if ext := ExtensionFromMIME("image/jpeg"); ext != "jpg" { t.Fatalf("jpg extension mismatch: %s", ext) } if ext := ExtensionFromMIME("image/png"); ext != "png" { t.Fatalf("png extension mismatch: %s", ext) } if ext := ExtensionFromMIME("image/gif"); ext != "gif" { t.Fatalf("gif extension mismatch: %s", ext) } if ext := ExtensionFromMIME("video/mp4"); ext != "mp4" { t.Fatalf("mp4 extension mismatch: %s", ext) } if ext := ExtensionFromMIME("application/octet-stream"); ext != "bin" { t.Fatalf("bin extension mismatch: %s", ext) } } func TestIsImage(t *testing.T) { if !IsImage("image/jpeg") { t.Fatal("expected jpeg to be image") } if IsImage("video/mp4") { t.Fatal("expected mp4 to not be image") } } func TestIsVideo(t *testing.T) { if !IsVideo("video/mp4") { t.Fatal("expected mp4 to be video") } if IsVideo("image/jpeg") { t.Fatal("expected jpeg to not be video") } } func createTestJPEG(t *testing.T, w, h int) []byte { t.Helper() img := image.NewRGBA(image.Rect(0, 0, w, h)) for y := range h { for x := range w { img.Set(x, y, color.RGBA{uint8(x % 256), uint8(y % 256), 128, 255}) } } var buf bytes.Buffer if err := jpeg.Encode(&buf, img, nil); err != nil { t.Fatalf("encode test jpeg: %v", err) } return buf.Bytes() } func TestGenerateThumbnail_JPEG(t *testing.T) { data := createTestJPEG(t, 800, 600) thumb, err := GenerateThumbnail(data, ThumbnailMaxDimension) if err != nil { t.Fatalf("GenerateThumbnail: %v", err) } if len(thumb) == 0 { t.Fatal("expected non-empty thumbnail") } // Decode and verify dimensions. img, _, err := image.Decode(bytes.NewReader(thumb)) if err != nil { t.Fatalf("decode thumbnail: %v", err) } bounds := img.Bounds() if bounds.Dx() > ThumbnailMaxDimension || bounds.Dy() > ThumbnailMaxDimension { t.Fatalf("thumbnail too large: %dx%d", bounds.Dx(), bounds.Dy()) } } func TestGenerateThumbnail_SmallImage(t *testing.T) { data := createTestJPEG(t, 50, 50) thumb, err := GenerateThumbnail(data, ThumbnailMaxDimension) if err != nil { t.Fatalf("GenerateThumbnail: %v", err) } if len(thumb) == 0 { t.Fatal("expected non-empty thumbnail for small image") } } func TestGenerateThumbnail_PNG(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 400, 300)) var buf bytes.Buffer png.Encode(&buf, img) thumb, err := GenerateThumbnail(buf.Bytes(), ThumbnailMaxDimension) if err != nil { t.Fatalf("GenerateThumbnail PNG: %v", err) } if len(thumb) == 0 { t.Fatal("expected non-empty thumbnail") } } func TestGenerateThumbnail_InvalidFormat(t *testing.T) { _, err := GenerateThumbnail([]byte("not an image"), ThumbnailMaxDimension) if err == nil { t.Fatal("expected error for invalid image data") } } func TestThumbnailPath(t *testing.T) { if got := ThumbnailPath("ab/cd/ef/photo.jpg"); got != "ab/cd/ef/photo_thumb.jpg" { t.Fatalf("ThumbnailPath = %q", got) } if got := ThumbnailPath("single.jpg"); got != "single_thumb.jpg" { t.Fatalf("ThumbnailPath = %q", got) } } func TestDetectContentType_RealJPEG(t *testing.T) { data := createTestJPEG(t, 100, 100) mime := DetectContentType(data) if mime != "image/jpeg" { t.Fatalf("mime = %q, want image/jpeg", mime) } } func TestStripEXIF_NoEXIF(t *testing.T) { data := createTestJPEG(t, 100, 100) cleaned, err := StripEXIF(data) if err != nil { t.Fatalf("StripEXIF: %v", err) } if len(cleaned) == 0 { t.Fatal("expected non-empty cleaned data") } } func TestDecodeImage(t *testing.T) { data := createTestJPEG(t, 640, 480) cfg, _, err := DecodeImage(data) if err != nil { t.Fatalf("DecodeImage: %v", err) } if cfg.Bounds().Dx() != 640 { t.Fatalf("width = %d, want 640", cfg.Bounds().Dx()) } if cfg.Bounds().Dy() != 480 { t.Fatalf("height = %d, want 480", cfg.Bounds().Dy()) } } func TestDecodeImage_Invalid(t *testing.T) { _, _, err := DecodeImage([]byte("not an image")) if err == nil { t.Fatal("expected error for invalid image") } } func TestFFmpegAvailable(t *testing.T) { FFmpegAvailable() } func TestGenerateVideoThumbnail_Skipped(t *testing.T) { dir := t.TempDir() input := filepath.Join(dir, "test.mp4") output := filepath.Join(dir, "out.jpg") err := GenerateVideoThumbnail(input, output) if err == nil { t.Fatal("expected error for non-existent video") } }