package photos import ( "context" "image" "image/color" "image/jpeg" "image/png" "os" "path/filepath" "testing" "github.com/mithral/vellum/internal/media" ) func writePNG(t *testing.T, w, h int) string { t.Helper() p := filepath.Join(t.TempDir(), "photo.png") f, err := os.Create(p) if err != nil { t.Fatal(err) } defer f.Close() img := image.NewRGBA(image.Rect(0, 0, w, h)) for y := 0; y < h; y++ { for x := 0; x < w; x++ { img.Set(x, y, color.RGBA{uint8(x % 256), uint8(y % 256), 128, 255}) } } if err := png.Encode(f, img); err != nil { t.Fatal(err) } return p } func decode(t *testing.T, path string) image.Image { t.Helper() f, err := os.Open(path) if err != nil { t.Fatal(err) } defer f.Close() img, _, err := image.Decode(f) if err != nil { t.Fatal(err) } return img } func TestExtractCoverDownscales(t *testing.T) { src := writePNG(t, 1000, 1500) cacheDir := t.TempDir() item := media.Item{ID: 7, Kind: Kind} out, err := ExtractCover(context.Background(), nil, item, src, cacheDir) if err != nil { t.Fatalf("ExtractCover: %v", err) } if out == "" { t.Fatal("no thumbnail produced") } img := decode(t, out) b := img.Bounds() if b.Dx() > thumbMax || b.Dy() > thumbMax { t.Errorf("thumbnail is %dx%d, exceeds %d", b.Dx(), b.Dy(), thumbMax) } // Aspect ratio must be preserved: 1000x1500 -> 341x512. if b.Dy() != thumbMax { t.Errorf("long edge = %d, want %d", b.Dy(), thumbMax) } if b.Dx() != 341 { t.Errorf("short edge = %d, want 341", b.Dx()) } // It must actually be a JPEG. f, err := os.Open(out) if err != nil { t.Fatal(err) } defer f.Close() if _, err := jpeg.Decode(f); err != nil { t.Errorf("output is not a JPEG: %v", err) } } func TestExtractCoverLeavesSmallImagesAlone(t *testing.T) { src := writePNG(t, 100, 80) out, err := ExtractCover(context.Background(), nil, media.Item{ID: 1, Kind: Kind}, src, t.TempDir()) if err != nil { t.Fatalf("ExtractCover: %v", err) } img := decode(t, out) if img.Bounds().Dx() != 100 || img.Bounds().Dy() != 80 { t.Errorf("small image was resized to %v", img.Bounds()) } } func TestExtractCoverIgnoresNonImage(t *testing.T) { p := filepath.Join(t.TempDir(), "not-an-image.jpg") if err := os.WriteFile(p, []byte("definitely not an image"), 0o644); err != nil { t.Fatal(err) } out, err := ExtractCover(context.Background(), nil, media.Item{ID: 1, Kind: Kind}, p, t.TempDir()) if err != nil { t.Fatalf("a non-image must not error: %v", err) } if out != "" { t.Errorf("expected no thumbnail, got %q", out) } }