Make web tests database-free and finish review hardening.
Introduce a Store interface with Postgres and in-memory backends, cover mutations/CSRF/session rotation without Postgres, bound avatar decode dimensions, add truncate/prepareAvatar unit tests, and run go test -race in CI.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
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)
|
||||
}
|
||||
|
||||
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: "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"},
|
||||
}
|
||||
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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
Reference in New Issue
Block a user