avatar_test.go
| 1 | package avatar |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "encoding/hex" |
| 6 | "image" |
| 7 | "image/color" |
| 8 | "image/jpeg" |
| 9 | "os" |
| 10 | "path/filepath" |
| 11 | "testing" |
| 12 | ) |
| 13 | |
| 14 | // Reference values from the previous implementation. They pin the identicon |
| 15 | // algorithm, so existing users keep the same picture. |
| 16 | func TestIdenticonMatchesReference(t *testing.T) { |
| 17 | tests := []struct { |
| 18 | name string |
| 19 | bytes string |
| 20 | rgb [3]uint8 |
| 21 | }{ |
| 22 | {"alice", "a554a5a173394418a17c66ae57445638", [3]uint8{156, 178, 31}}, |
| 23 | {"admin", "7c212d2be8af0b70a0dd6542c4f55b32", [3]uint8{166, 33, 186}}, |
| 24 | {"bob-42", "07c21f2cb96afe78a3f481297d2f9406", [3]uint8{197, 27, 53}}, |
| 25 | {"ünïcode", "6f73f4fe71b1d1c57b611ae2556e8766", [3]uint8{219, 140, 57}}, |
| 26 | } |
| 27 | for _, tt := range tests { |
| 28 | b := hashBytes(tt.name, 16) |
| 29 | if got := hex.EncodeToString(b); got != tt.bytes { |
| 30 | t.Errorf("hashBytes(%q) = %s, want %s", tt.name, got, tt.bytes) |
| 31 | } |
| 32 | r, g, bl := hslToRGB(float64((int(b[0])|int(b[1])<<8)%360), float64(int(b[2])%20+65), float64(int(b[3])%20+40)) |
| 33 | if [3]uint8{r, g, bl} != tt.rgb { |
| 34 | t.Errorf("color(%q) = %v, want %v", tt.name, [3]uint8{r, g, bl}, tt.rgb) |
| 35 | } |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | func TestIdenticonPixels(t *testing.T) { |
| 40 | img := Identicon("alice") |
| 41 | if img.Bounds() != image.Rect(0, 0, 128, 128) { |
| 42 | t.Fatalf("bounds = %v", img.Bounds()) |
| 43 | } |
| 44 | // The padding area is always background. |
| 45 | if got := img.NRGBAAt(0, 0); got != (color.NRGBA{240, 240, 240, 255}) { |
| 46 | t.Errorf("corner = %v", got) |
| 47 | } |
| 48 | // Column 0 and column 4 mirror each other. |
| 49 | for y := 24; y < 104; y++ { |
| 50 | if img.NRGBAAt(24, y) != img.NRGBAAt(127-24, y) { |
| 51 | t.Fatalf("identicon not symmetric at y=%d", y) |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | func TestSaveResizesAndRejectsNonImages(t *testing.T) { |
| 57 | dir := t.TempDir() |
| 58 | |
| 59 | src := image.NewRGBA(image.Rect(0, 0, 400, 200)) |
| 60 | var buf bytes.Buffer |
| 61 | if err := jpeg.Encode(&buf, src, nil); err != nil { |
| 62 | t.Fatal(err) |
| 63 | } |
| 64 | if err := Save(dir, 7, buf.Bytes()); err != nil { |
| 65 | t.Fatal(err) |
| 66 | } |
| 67 | f, err := os.Open(filepath.Join(dir, "7.png")) |
| 68 | if err != nil { |
| 69 | t.Fatal(err) |
| 70 | } |
| 71 | defer f.Close() |
| 72 | cfg, format, err := image.DecodeConfig(f) |
| 73 | if err != nil { |
| 74 | t.Fatal(err) |
| 75 | } |
| 76 | if format != "png" || cfg.Width != 128 || cfg.Height != 128 { |
| 77 | t.Errorf("stored %s %dx%d", format, cfg.Width, cfg.Height) |
| 78 | } |
| 79 | |
| 80 | if err := Save(dir, 8, []byte("not an image at all")); err == nil { |
| 81 | t.Error("expected an error for a non-image upload") |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | func TestPath(t *testing.T) { |
| 86 | if got := Path("/data/avatars", 42); got != "/data/avatars/42.png" { |
| 87 | t.Errorf("Path = %q", got) |
| 88 | } |
| 89 | } |
| 90 |