Files
perinco-Pulse/server/test/avatar.test.mjs
T
schakal00783andClaude Opus 5 9576d18abc feat(profil): eigenes Profilbild hochladen
Jeder setzt sein Bild unter "Mein Konto", Vorgesetzte und Administratoren
auch fuer die Benutzer, die sie verwalten. Wo bisher die Initialen standen,
steht jetzt das Bild - Seitenleiste, Kopfzeile, Team, Dashboard, Reports,
Projekte, Kunden und die Buchungsliste. Ohne Bild bleiben die Initialen.

Der Browser verkleinert das Bild vor dem Hochladen auf ein Quadrat von
256 px und schneidet mittig zu (lib/imageCompress.ts): aus einem 4-MB-Foto
werden wenige Kilobyte, und der Server muss kein Bild verarbeiten.

Gespeichert wird es als Data-URL in pulse_users.avatar (LONGTEXT). Kein
Upload-Verzeichnis, das gesichert und beim Deployment am Leben gehalten
werden muesste.

Geprueft wird serverseitig in lib/avatar.js: nur PNG, JPEG, GIF, WebP und
AVIF als Data-URL, hoechstens 2 MB. SVG ist bewusst nicht erlaubt, es kann
Skripte tragen. Externe http(s)-Adressen ebenso wenig - sonst laedt jeder
Seitenaufruf ein Bild von einem fremden Server und verraet, wer wann mit
Pulse arbeitet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:06:02 +02:00

38 lines
1.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { normalizeAvatar, AVATAR_MAX_LENGTH } from '../src/lib/avatar.js';
const PNG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUg==';
test('nimmt Bild-Data-URLs an', () => {
for (const typ of ['png', 'jpeg', 'jpg', 'gif', 'webp', 'avif']) {
const url = `data:image/${typ};base64,AAAA`;
assert.equal(normalizeAvatar(url).value, url, typ);
}
});
test('leer entfernt das Bild', () => {
for (const wert of [null, undefined, '', ' ']) {
assert.deepEqual(normalizeAvatar(wert), { value: null });
}
});
test('weist alles ab, was kein Bild ist', () => {
// SVG kann Skripte tragen, eine externe Adresse verraet die Benutzung nach draussen.
for (const wert of [
'data:image/svg+xml;base64,PHN2Zy8+',
'data:text/html;base64,PGgxPmhpPC9oMT4',
'https://example.com/foto.png',
'javascript:alert(1)',
'hallo',
]) {
assert.ok(normalizeAvatar(wert).error, wert);
}
});
test('begrenzt die Groesse', () => {
const zuGross = `data:image/png;base64,${'A'.repeat(AVATAR_MAX_LENGTH)}`;
assert.match(normalizeAvatar(zuGross).error, /zu groß/);
assert.equal(normalizeAvatar(PNG).error, undefined);
});