package media import ( "bytes" "image" "image/color" "image/jpeg" "testing" pkgmedia "spark-server/internal/pkg/media" ) func TestCompressImageDownscales(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 2000, 1000)) for y := 0; y < 1000; y++ { for x := 0; x < 2000; x++ { img.Set(x, y, color.RGBA{R: uint8(x % 255), G: uint8(y % 255), B: 40, A: 255}) } } src, err := EncodePNG(img) if err != nil { t.Fatal(err) } opt := &Options{Limits: pkgmedia.DefaultLimits(), JPEGQuality: 80} res, err := CompressImage(src, opt) if err != nil { t.Fatal(err) } if pkgmedia.LongestEdge(res.Width, res.Height) > pkgmedia.DefaultImageMaxEdge { t.Fatalf("edge %dx%d still over 1080", res.Width, res.Height) } if int64(len(res.Data)) > pkgmedia.DefaultImageMaxBytes { t.Fatalf("size %d > %d", len(res.Data), pkgmedia.DefaultImageMaxBytes) } if res.MIME != "image/jpeg" { t.Fatalf("mime %s", res.MIME) } small := image.NewRGBA(image.Rect(0, 0, 100, 80)) smallSrc, err := EncodePNG(small) if err != nil { t.Fatal(err) } def := &Options{Limits: pkgmedia.DefaultLimits()} keep, err := CompressImage(smallSrc, def) if err != nil { t.Fatal(err) } if keep.Width != 100 || keep.Height != 80 { t.Fatalf("should keep small image dims, got %dx%d", keep.Width, keep.Height) } } func TestCompressImageUnder150KiB(t *testing.T) { src := mustOversizedJPEG(t, int(pkgmedia.DefaultImageMaxBytes)+1) res, err := CompressImage(src, DefaultOptions()) if err != nil { t.Fatal(err) } if int64(len(res.Data)) > pkgmedia.DefaultImageMaxBytes { t.Fatalf("after compress %d > %d", len(res.Data), pkgmedia.DefaultImageMaxBytes) } out, mime, err := EnsureUnderLimit(src, "image/jpeg", nil) if err != nil { t.Fatal(err) } if int64(len(out)) > pkgmedia.DefaultImageMaxBytes { t.Fatalf("EnsureUnderLimit %d", len(out)) } if mime != "image/jpeg" { t.Fatalf("mime %s", mime) } } // mustOversizedJPEG grows a noisy JPEG until len >= minBytes (JPEG of smooth // gradients can stay tiny; use per-pixel noise and bump resolution as needed). func mustOversizedJPEG(t *testing.T, minBytes int) []byte { t.Helper() for side := 800; side <= 4800; side += 400 { img := image.NewRGBA(image.Rect(0, 0, side, side*3/4)) w, h := img.Bounds().Dx(), img.Bounds().Dy() for y := 0; y < h; y++ { for x := 0; x < w; x++ { n := uint32(x*374761393 + y*668265263 + side*97) n ^= n << 13 n ^= n >> 17 n ^= n << 5 img.Set(x, y, color.RGBA{R: uint8(n), G: uint8(n >> 8), B: uint8(n >> 16), A: 255}) } } var buf bytes.Buffer if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: 100}); err != nil { t.Fatal(err) } if buf.Len() >= minBytes { return buf.Bytes() } } t.Fatalf("could not synthesize JPEG >= %d bytes", minBytes) return nil } func TestCompressAvatar(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 500, 500)) src, err := EncodePNG(img) if err != nil { t.Fatal(err) } res, err := CompressAvatar(src, &Options{Limits: pkgmedia.DefaultLimits()}) if err != nil { t.Fatal(err) } if pkgmedia.LongestEdge(res.Width, res.Height) > pkgmedia.DefaultAvatarMaxEdge { t.Fatalf("avatar %dx%d", res.Width, res.Height) } if int64(len(res.Data)) > pkgmedia.DefaultAvatarMaxBytes { t.Fatalf("avatar size %d", len(res.Data)) } } func TestPrepareBotImage(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 1600, 900)) src, err := EncodePNG(img) if err != nil { t.Fatal(err) } a, err := PrepareBotImage(src, &Options{Limits: pkgmedia.DefaultLimits()}) if err != nil || a == nil || a.Size <= 0 { t.Fatalf("%+v %v", a, err) } if a.Size > pkgmedia.DefaultImageMaxBytes { t.Fatalf("bot image %d > 150KiB", a.Size) } } func TestIsImageMIME(t *testing.T) { if !IsImageMIME("image/jpeg") || !IsImageMIME("image/png; charset=binary") { t.Fatal("want image") } if IsImageMIME("audio/mpeg") || IsImageMIME("video/mp4") { t.Fatal("reject non-image") } }