package web import ( "bytes" "encoding/binary" "hash/crc32" "image" "image/jpeg" "image/png" "strings" "testing" ) func TestTruncateRunes(t *testing.T) { tests := []struct { in string max int want string }{ {"abc", 10, "abc"}, {"abcdef", 3, "abc"}, {"hΓ©llo", 3, "hΓ©l"}, {"πŸ™‚πŸ™‚πŸ™‚", 2, "πŸ™‚πŸ™‚"}, {"δΈ–η•Œε’ŒεΉ³", 2, "δΈ–η•Œ"}, {"abc", 0, ""}, {"abc", -1, ""}, {"", 5, ""}, } for _, tc := range tests { if got := truncateRunes(tc.in, tc.max); got != tc.want { t.Fatalf("truncateRunes(%q, %d)=%q want %q", tc.in, tc.max, got, tc.want) } } } func TestPrepareAvatar(t *testing.T) { var pngBuf bytes.Buffer if err := png.Encode(&pngBuf, image.NewRGBA(image.Rect(0, 0, 2, 2))); err != nil { t.Fatal(err) } var jpegBuf bytes.Buffer if err := jpeg.Encode(&jpegBuf, image.NewRGBA(image.Rect(0, 0, 2, 2)), &jpeg.Options{Quality: 90}); err != nil { t.Fatal(err) } var largePNG bytes.Buffer if err := png.Encode(&largePNG, image.NewRGBA(image.Rect(0, 0, 800, 600))); err != nil { t.Fatal(err) } oversized := bytes.Repeat([]byte{0x89}, (2<<20)+2) tests := []struct { name string in []byte max int64 wantExt string wantErr string }{ {name: "png", in: pngBuf.Bytes(), max: 2 << 20, wantExt: ".png"}, {name: "jpeg", in: jpegBuf.Bytes(), max: 2 << 20, wantExt: ".jpg"}, {name: "resize large", in: largePNG.Bytes(), max: 2 << 20, wantExt: ".png"}, {name: "empty", in: nil, max: 2 << 20, wantErr: "empty"}, {name: "invalid", in: []byte("not-an-image"), max: 2 << 20, wantErr: "unsupported"}, {name: "oversized", in: oversized, max: 2 << 20, wantErr: "too large"}, {name: "huge dims", in: pngWithDims(100000, 100000), max: 2 << 20, wantErr: "dimensions"}, {name: "over decode cap", in: pngWithDims(2048, 2048), max: 2 << 20, wantErr: "dimensions"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { body, ext, ct, err := prepareAvatar(bytes.NewReader(tc.in), tc.max) if tc.wantErr != "" { if err == nil || !strings.Contains(err.Error(), tc.wantErr) { t.Fatalf("err=%v want substring %q", err, tc.wantErr) } return } if err != nil { t.Fatal(err) } if ext != tc.wantExt { t.Fatalf("ext=%q want %q", ext, tc.wantExt) } if len(body) == 0 || ct == "" { t.Fatalf("empty output body/ct") } if int64(len(body)) > tc.max { t.Fatalf("encoded size %d exceeds max %d", len(body), tc.max) } cfg, _, err := image.DecodeConfig(bytes.NewReader(body)) if err != nil { t.Fatal(err) } if cfg.Width > 512 || cfg.Height > 512 { t.Fatalf("avatar dims %dx%d exceed 512", cfg.Width, cfg.Height) } }) } } func pngWithDims(w, h int) []byte { var buf bytes.Buffer buf.Write([]byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}) var ihdr bytes.Buffer _ = binary.Write(&ihdr, binary.BigEndian, uint32(w)) _ = binary.Write(&ihdr, binary.BigEndian, uint32(h)) ihdr.Write([]byte{8, 2, 0, 0, 0}) // bit depth, color type, compression, filter, interlace writePNGChunk(&buf, "IHDR", ihdr.Bytes()) writePNGChunk(&buf, "IDAT", []byte{0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01}) writePNGChunk(&buf, "IEND", nil) return buf.Bytes() } func writePNGChunk(buf *bytes.Buffer, name string, data []byte) { _ = binary.Write(buf, binary.BigEndian, uint32(len(data))) buf.WriteString(name) buf.Write(data) crc := crc32.NewIEEE() _, _ = crc.Write([]byte(name)) _, _ = crc.Write(data) _ = binary.Write(buf, binary.BigEndian, crc.Sum32()) }