internal/storage/processing_test.go (view raw)
1package storage
2
3import (
4 "bytes"
5 "image"
6 "image/color"
7 "image/jpeg"
8 "image/png"
9 "path/filepath"
10 "testing"
11)
12
13func TestDetectContentType(t *testing.T) {
14 tests := []struct {
15 name string
16 data []byte
17 want string
18 }{
19 {"JPEG", []byte{0xFF, 0xD8, 0xFF, 0xE0}, "image/jpeg"},
20 {"PNG", []byte{0x89, 0x50, 0x4E, 0x47}, "image/png"},
21 {"GIF", []byte{0x47, 0x49, 0x46, 0x38}, "image/gif"},
22 {"WebP", []byte{0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50}, "image/webp"},
23 {"BMP", []byte{0x42, 0x4D}, "image/bmp"},
24 {"MP4", []byte{0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70}, "video/mp4"},
25 {"WebM", []byte{0x1A, 0x45, 0xDF, 0xA3}, "video/webm"},
26 {"unknown", []byte{0x00, 0x01, 0x02}, "application/octet-stream"},
27 {"empty", []byte{}, "application/octet-stream"},
28 }
29 for _, tt := range tests {
30 t.Run(tt.name, func(t *testing.T) {
31 got := DetectContentType(tt.data)
32 if got != tt.want {
33 t.Fatalf("DetectContentType = %q, want %q", got, tt.want)
34 }
35 })
36 }
37}
38
39func TestExtensionFromMIME(t *testing.T) {
40 if ext := ExtensionFromMIME("image/jpeg"); ext != "jpg" {
41 t.Fatalf("jpg extension mismatch: %s", ext)
42 }
43 if ext := ExtensionFromMIME("image/png"); ext != "png" {
44 t.Fatalf("png extension mismatch: %s", ext)
45 }
46 if ext := ExtensionFromMIME("image/gif"); ext != "gif" {
47 t.Fatalf("gif extension mismatch: %s", ext)
48 }
49 if ext := ExtensionFromMIME("video/mp4"); ext != "mp4" {
50 t.Fatalf("mp4 extension mismatch: %s", ext)
51 }
52 if ext := ExtensionFromMIME("application/octet-stream"); ext != "bin" {
53 t.Fatalf("bin extension mismatch: %s", ext)
54 }
55}
56
57func TestIsImage(t *testing.T) {
58 if !IsImage("image/jpeg") {
59 t.Fatal("expected jpeg to be image")
60 }
61 if IsImage("video/mp4") {
62 t.Fatal("expected mp4 to not be image")
63 }
64}
65
66func TestIsVideo(t *testing.T) {
67 if !IsVideo("video/mp4") {
68 t.Fatal("expected mp4 to be video")
69 }
70 if IsVideo("image/jpeg") {
71 t.Fatal("expected jpeg to not be video")
72 }
73}
74
75func createTestJPEG(t *testing.T, w, h int) []byte {
76 t.Helper()
77 img := image.NewRGBA(image.Rect(0, 0, w, h))
78 for y := range h {
79 for x := range w {
80 img.Set(x, y, color.RGBA{uint8(x % 256), uint8(y % 256), 128, 255})
81 }
82 }
83 var buf bytes.Buffer
84 if err := jpeg.Encode(&buf, img, nil); err != nil {
85 t.Fatalf("encode test jpeg: %v", err)
86 }
87 return buf.Bytes()
88}
89
90func TestGenerateThumbnail_JPEG(t *testing.T) {
91 data := createTestJPEG(t, 800, 600)
92 thumb, err := GenerateThumbnail(data, ThumbnailMaxDimension)
93 if err != nil {
94 t.Fatalf("GenerateThumbnail: %v", err)
95 }
96 if len(thumb) == 0 {
97 t.Fatal("expected non-empty thumbnail")
98 }
99
100 // Decode and verify dimensions.
101 img, _, err := image.Decode(bytes.NewReader(thumb))
102 if err != nil {
103 t.Fatalf("decode thumbnail: %v", err)
104 }
105 bounds := img.Bounds()
106 if bounds.Dx() > ThumbnailMaxDimension || bounds.Dy() > ThumbnailMaxDimension {
107 t.Fatalf("thumbnail too large: %dx%d", bounds.Dx(), bounds.Dy())
108 }
109}
110
111func TestGenerateThumbnail_SmallImage(t *testing.T) {
112 data := createTestJPEG(t, 50, 50)
113 thumb, err := GenerateThumbnail(data, ThumbnailMaxDimension)
114 if err != nil {
115 t.Fatalf("GenerateThumbnail: %v", err)
116 }
117 if len(thumb) == 0 {
118 t.Fatal("expected non-empty thumbnail for small image")
119 }
120}
121
122func TestGenerateThumbnail_PNG(t *testing.T) {
123 img := image.NewRGBA(image.Rect(0, 0, 400, 300))
124 var buf bytes.Buffer
125 png.Encode(&buf, img)
126 thumb, err := GenerateThumbnail(buf.Bytes(), ThumbnailMaxDimension)
127 if err != nil {
128 t.Fatalf("GenerateThumbnail PNG: %v", err)
129 }
130 if len(thumb) == 0 {
131 t.Fatal("expected non-empty thumbnail")
132 }
133}
134
135func TestGenerateThumbnail_InvalidFormat(t *testing.T) {
136 _, err := GenerateThumbnail([]byte("not an image"), ThumbnailMaxDimension)
137 if err == nil {
138 t.Fatal("expected error for invalid image data")
139 }
140}
141
142func TestThumbnailPath(t *testing.T) {
143 if got := ThumbnailPath("ab/cd/ef/photo.jpg"); got != "ab/cd/ef/photo_thumb.jpg" {
144 t.Fatalf("ThumbnailPath = %q", got)
145 }
146 if got := ThumbnailPath("single.jpg"); got != "single_thumb.jpg" {
147 t.Fatalf("ThumbnailPath = %q", got)
148 }
149}
150
151func TestDetectContentType_RealJPEG(t *testing.T) {
152 data := createTestJPEG(t, 100, 100)
153 mime := DetectContentType(data)
154 if mime != "image/jpeg" {
155 t.Fatalf("mime = %q, want image/jpeg", mime)
156 }
157}
158
159func TestStripEXIF_NoEXIF(t *testing.T) {
160 data := createTestJPEG(t, 100, 100)
161 cleaned, err := StripEXIF(data)
162 if err != nil {
163 t.Fatalf("StripEXIF: %v", err)
164 }
165 if len(cleaned) == 0 {
166 t.Fatal("expected non-empty cleaned data")
167 }
168}
169
170func TestDecodeImage(t *testing.T) {
171 data := createTestJPEG(t, 640, 480)
172 cfg, _, err := DecodeImage(data)
173 if err != nil {
174 t.Fatalf("DecodeImage: %v", err)
175 }
176 if cfg.Bounds().Dx() != 640 {
177 t.Fatalf("width = %d, want 640", cfg.Bounds().Dx())
178 }
179 if cfg.Bounds().Dy() != 480 {
180 t.Fatalf("height = %d, want 480", cfg.Bounds().Dy())
181 }
182}
183
184func TestDecodeImage_Invalid(t *testing.T) {
185 _, _, err := DecodeImage([]byte("not an image"))
186 if err == nil {
187 t.Fatal("expected error for invalid image")
188 }
189}
190
191func TestFFmpegAvailable(t *testing.T) {
192 FFmpegAvailable()
193}
194
195func TestGenerateVideoThumbnail_Skipped(t *testing.T) {
196 dir := t.TempDir()
197 input := filepath.Join(dir, "test.mp4")
198 output := filepath.Join(dir, "out.jpg")
199
200 err := GenerateVideoThumbnail(input, output)
201 if err == nil {
202 t.Fatal("expected error for non-existent video")
203 }
204}