Files
Aether/backend/internal/admin/appicon_test.go
T
HoneyandClaude Opus 5 31a027c93a feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 09:25:36 +02:00

123 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package admin
import (
"bytes"
"image"
"image/color"
"image/png"
"testing"
)
func testImage(w, h int) []byte {
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := range h {
for x := range w {
img.Set(x, y, color.RGBA{R: 20, G: 180, B: 120, A: 255})
}
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
panic(err)
}
return buf.Bytes()
}
func decodeSize(t *testing.T, blob []byte) (int, int) {
t.Helper()
img, err := png.Decode(bytes.NewReader(blob))
if err != nil {
t.Fatalf("decode result: %v", err)
}
return img.Bounds().Dx(), img.Bounds().Dy()
}
var testBG = color.RGBA{R: 31, G: 107, B: 92, A: 255}
func TestRenderAppIconSizes(t *testing.T) {
out, err := renderAppIcon(bytes.NewReader(testImage(1000, 1000)), testBG)
if err != nil {
t.Fatalf("render: %v", err)
}
for name, want := range map[string]int{
"64.png": 64, "192.png": 192, "512.png": 512, "maskable.png": 512,
} {
blob, ok := out[name]
if !ok {
t.Fatalf("%s missing", name)
}
if w, h := decodeSize(t, blob); w != want || h != want {
t.Errorf("%s: got %dx%d, want %dx%d", name, w, h, want, want)
}
}
}
// Aus einer kleinen Vorlage darf kein 512er entstehen – hochskaliert sähe es
// schlechter aus als gar keins, und das Manifest nennt dann nur, was es gibt.
func TestRenderAppIconNeverUpscales(t *testing.T) {
out, err := renderAppIcon(bytes.NewReader(testImage(256, 256)), testBG)
if err != nil {
t.Fatalf("render: %v", err)
}
if _, ok := out["512.png"]; ok {
t.Error("512.png was generated from a 256px source")
}
if w, _ := decodeSize(t, out["maskable.png"]); w != 192 {
t.Errorf("maskable: got %d, want 192", w)
}
}
func TestRenderAppIconRejectsSmall(t *testing.T) {
if _, err := renderAppIcon(bytes.NewReader(testImage(100, 100)), testBG); err == nil {
t.Fatal("expected an error for a 100px source")
}
}
// Nicht quadratische Vorlagen werden gerahmt, nicht beschnitten: Ein Zuschnitt
// würde ein Logo anschneiden, und das fällt erst auf, wenn das Symbol klebt.
func TestRenderAppIconFramesInsteadOfCropping(t *testing.T) {
out, err := renderAppIcon(bytes.NewReader(testImage(400, 150)), testBG)
if err != nil {
t.Fatalf("render: %v", err)
}
img, err := png.Decode(bytes.NewReader(out["192.png"]))
if err != nil {
t.Fatalf("decode: %v", err)
}
if _, _, _, a := img.At(96, 2).RGBA(); a != 0 {
t.Error("top edge should be transparent padding, not image content")
}
if _, g, _, a := img.At(96, 96).RGBA(); a == 0 || g>>8 < 150 {
t.Error("centre should carry the motif")
}
}
// Der maskable-Grund ist die Primärfarbe: Durchsichtig füllt Android mit Weiß,
// und Weiß leuchtet im dunklen App-Menü.
func TestRenderAppIconMaskableHasBackground(t *testing.T) {
out, err := renderAppIcon(bytes.NewReader(testImage(512, 512)), testBG)
if err != nil {
t.Fatalf("render: %v", err)
}
img, err := png.Decode(bytes.NewReader(out["maskable.png"]))
if err != nil {
t.Fatalf("decode: %v", err)
}
r, g, b, a := img.At(2, 2).RGBA()
if a>>8 != 255 || r>>8 != 31 || g>>8 != 107 || b>>8 != 92 {
t.Errorf("corner: got %d,%d,%d,%d – want the background colour", r>>8, g>>8, b>>8, a>>8)
}
}
func TestParseHexColor(t *testing.T) {
fallback := color.RGBA{R: 1, G: 2, B: 3, A: 255}
if got := parseHexColor("#1f6b5c", fallback); got != (color.RGBA{R: 31, G: 107, B: 92, A: 255}) {
t.Errorf("valid colour: got %v", got)
}
// Eine kaputte Einstellung darf keinen Upload scheitern lassen.
for _, bad := range []string{"", "1f6b5c", "#xyz", "#1f6b5cff"} {
if got := parseHexColor(bad, fallback); got != fallback {
t.Errorf("%q: got %v, want the fallback", bad, got)
}
}
}