package webp_test import ( "bytes" "errors" "image" "image/color" "image/draw" "io" "strings" "testing" "git.golem15.com/golem15/summercms/modules/lagoon/attach" "git.golem15.com/golem15/summercms/modules/lagoon/attach/webp" "gocloud.dev/blob/memblob" ) // TestEncodeImageKinds pins Encode on the image kinds a decoder hands it: // RGBA, NRGBA, grey, paletted and YCbCr pictures all come out as a WebP of // the same size, and a 1x1 picture too. func TestEncodeImageKinds(t *testing.T) { rgba := gradient(48, 30) nrgba := image.NewNRGBA(rgba.Bounds()) draw.Draw(nrgba, nrgba.Bounds(), rgba, image.Point{}, draw.Src) gray := image.NewGray(rgba.Bounds()) draw.Draw(gray, gray.Bounds(), rgba, image.Point{}, draw.Src) pal := image.NewPaletted(rgba.Bounds(), color.Palette{color.Black, color.White, color.RGBA{200, 20, 20, 255}}) draw.Draw(pal, pal.Bounds(), rgba, image.Point{}, draw.Src) ycc := image.NewYCbCr(rgba.Bounds(), image.YCbCrSubsampleRatio420) one := image.NewRGBA(image.Rect(0, 0, 1, 1)) for name, img := range map[string]image.Image{"rgba": rgba, "nrgba": nrgba, "gray": gray, "paletted": pal, "ycbcr": ycc, "1x1": one} { var buf bytes.Buffer if err := webp.Encode(&buf, img); err != nil { t.Fatalf("%s: %v", name, err) } cfg, format, err := image.DecodeConfig(bytes.NewReader(buf.Bytes())) b := img.Bounds() if err != nil || format != "webp" || cfg.Width != b.Dx() || cfg.Height != b.Dy() || !isWebP(buf.Bytes()) { t.Fatalf("%s: decoded %+v %q err=%v, want a %dx%d webp", name, cfg, format, err, b.Dx(), b.Dy()) } } } // TestEncodeConstants pins the variant's format, content type and quality. func TestEncodeConstants(t *testing.T) { if webp.Format != "webp" || webp.ContentType != "image/webp" || webp.Quality < 1 || webp.Quality > 100 { t.Fatalf("Format %q ContentType %q Quality %d", webp.Format, webp.ContentType, webp.Quality) } } // TestVariantStoredWithContentType pins the stored variant's content type // and that the variant key is the thumbnail's VariantKey, and that a // second call answers the same key without touching the stored bytes. func TestVariantStoredWithContentType(t *testing.T) { bucket := memblob.OpenBucket(nil) defer bucket.Close() f := &attach.File{ID: 11, DiskName: "jkl012mno345pqr678stu9.jpg"} putOriginal(t, bucket, f, 320, 200) key, err := webp.Variant(t.Context(), bucket, f, 160, 100, "exact") if err != nil { t.Fatal(err) } thumb, err := f.ThumbKey(t.Context(), bucket, 160, 100, "exact") if err != nil || key != attach.VariantKey(thumb, webp.Format) { t.Fatalf("key %q, thumbnail %q err=%v", key, thumb, err) } attrs, err := bucket.Attributes(t.Context(), key) if err != nil || attrs.ContentType != webp.ContentType { t.Fatalf("attributes %+v err=%v, want image/webp", attrs, err) } before, _ := bucket.ReadAll(t.Context(), key) again, err := webp.Variant(t.Context(), bucket, f, 160, 100, "exact") after, _ := bucket.ReadAll(t.Context(), key) if err != nil || again != key || !bytes.Equal(before, after) { t.Fatalf("second call %q err=%v, bytes changed %v", again, err, !bytes.Equal(before, after)) } } // TestVariantBrokenAndRefused pins the edges: a missing original gets // ThumbKey's broken-image thumbnail, whose variant is still a WebP; a mode // outside the token rule is refused and writes no variant. func TestVariantBrokenAndRefused(t *testing.T) { bucket := memblob.OpenBucket(nil) defer bucket.Close() missing := &attach.File{ID: 12, DiskName: "vwx901yza234bcd567efg8.jpg"} key, err := webp.Variant(t.Context(), bucket, missing, 100, 100, "exact") if err != nil { t.Fatal(err) } if raw, err := bucket.ReadAll(t.Context(), key); err != nil || !isWebP(raw) { t.Fatalf("variant of the broken-image thumbnail %q: err=%v", key, err) } good := &attach.File{ID: 14, DiskName: "tuv345wxy678zab901cde2.jpg"} putOriginal(t, bucket, good, 200, 200) if _, err := webp.Variant(t.Context(), bucket, good, 100, 100, "../x"); err == nil { t.Fatal("a mode outside the token rule must fail") } iter := bucket.List(nil) webps := 0 for { obj, err := iter.Next(t.Context()) if errors.Is(err, io.EOF) { break } if err != nil { t.Fatal(err) } if strings.HasSuffix(obj.Key, ".webp") { webps++ } } if webps != 1 { t.Fatalf("%d WebP files in the bucket, want only the broken-image variant", webps) } }