- Router: Routen home, gestalten, cards, community/<id>, reset; Umleitung /?karte=<id> nach /gestalten?karte=<id>; Titel je Route (titleFor); Hash-Ziele (/#community) scrollen den Anker ins Bild, nur-Hash-Wechsel bauen die Seite nicht neu auf. - Kopfzeile: Navigation, Suche (Enter -> /?q=...#community), Spenden-Knopf, Hell/Dunkel-Schalter, Menue unter 900 px, Konto als Avatar-Kreis mit Menue. - Theme: data-theme auf <html> (Skript im <head> gegen Aufblitzen), helle Tokens, danger-/warn-/ok-text-Tokens statt fester Farben. - Spenden-Banner (schliessbar); ohne DONATE_URL bleiben Banner und Knopf weg. - User.labels und isAdmin; Platzhalter fuer Startseite und Detailseite. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
411 lines
18 KiB
TypeScript
411 lines
18 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
get: vi.fn(),
|
|
create: vi.fn(),
|
|
createEmailPasswordSession: vi.fn(),
|
|
deleteSession: vi.fn(),
|
|
createRecovery: vi.fn(),
|
|
updateRecovery: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('./appwrite', () => ({ account: mocks }));
|
|
|
|
type Auth = typeof import('./auth');
|
|
|
|
const fail = (code: number, type: string, message = '') => Object.assign(new Error(message), { code, type });
|
|
|
|
// Der Nutzer-Cache lebt im Modul; jeder Test bekommt eine frische Instanz.
|
|
async function freshAuth(): Promise<Auth> {
|
|
vi.resetModules();
|
|
return import('./auth');
|
|
}
|
|
|
|
beforeEach(() => {
|
|
for (const fn of Object.values(mocks)) fn.mockReset();
|
|
});
|
|
|
|
describe('getCurrentUser', () => {
|
|
it('liefert den Nutzer und merkt sich die ID', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de', name: 'a' });
|
|
await expect(auth.getCurrentUser()).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
expect(auth.currentUserId()).toBe('u1');
|
|
});
|
|
|
|
it('401 heißt: nicht angemeldet', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.getCurrentUser();
|
|
mocks.get.mockRejectedValueOnce(fail(401, 'general_unauthorized_scope'));
|
|
await expect(auth.getCurrentUser()).resolves.toBeNull();
|
|
expect(auth.currentUserId()).toBeNull();
|
|
});
|
|
|
|
it('currentUserId ist vor dem ersten Aufruf null', async () => {
|
|
const auth = await freshAuth();
|
|
expect(auth.currentUserId()).toBeNull();
|
|
});
|
|
|
|
it('eine Antwort ohne $id/email (z. B. HTML) ist ein Fehler und ändert den Cache nicht', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.getCurrentUser();
|
|
mocks.get.mockResolvedValueOnce({ message: '<!doctype html>' });
|
|
await expect(auth.getCurrentUser()).rejects.toThrow('Unerwartete Antwort');
|
|
expect(auth.currentUserId()).toBe('u1');
|
|
});
|
|
|
|
it('andere Fehler werden weitergereicht', async () => {
|
|
const auth = await freshAuth();
|
|
const error = new TypeError('Failed to fetch');
|
|
mocks.get.mockRejectedValue(error);
|
|
await expect(auth.getCurrentUser()).rejects.toBe(error);
|
|
});
|
|
|
|
it('benachrichtigt nur, wenn sich der Nutzer ändert', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.getCurrentUser();
|
|
await auth.getCurrentUser();
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
expect(listener).toHaveBeenLastCalledWith({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
mocks.get.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
|
await auth.getCurrentUser();
|
|
await auth.getCurrentUser();
|
|
expect(listener).toHaveBeenCalledTimes(2);
|
|
expect(listener).toHaveBeenLastCalledWith(null);
|
|
});
|
|
});
|
|
|
|
describe('Labels und isAdmin', () => {
|
|
it('getCurrentUser übernimmt die Labels; fehlen sie (oder sind sie kein Array aus Texten), sind es []', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin', 'x'] });
|
|
await expect(auth.getCurrentUser()).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: ['admin', 'x'] });
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de' });
|
|
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: [] });
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: 'admin' });
|
|
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: [] });
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin', 7, null] });
|
|
await expect(auth.getCurrentUser()).resolves.toMatchObject({ labels: ['admin'] });
|
|
});
|
|
|
|
it('login liefert die Labels ebenfalls', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
|
await expect(auth.login('a@b.de', 'geheim123')).resolves.toMatchObject({ labels: ['admin'] });
|
|
expect(auth.currentUser()?.labels).toEqual(['admin']);
|
|
});
|
|
|
|
it('eine Änderung nur der Labels benachrichtigt die Abonnenten', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
await auth.getCurrentUser();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
await auth.getCurrentUser();
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
|
await auth.getCurrentUser();
|
|
expect(listener).toHaveBeenCalledTimes(2);
|
|
expect(listener).toHaveBeenLastCalledWith({ $id: 'u1', email: 'a@b.de', labels: ['admin'] });
|
|
});
|
|
|
|
it('isAdmin ist nur mit dem Label „admin“ wahr', async () => {
|
|
const auth = await freshAuth();
|
|
expect(auth.isAdmin(null)).toBe(false);
|
|
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de' })).toBe(false);
|
|
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: [] })).toBe(false);
|
|
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: ['administrator', 'Admin'] })).toBe(false);
|
|
expect(auth.isAdmin({ $id: 'u1', email: 'a@b.de', labels: ['x', 'admin'] })).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('login', () => {
|
|
it('legt die Sitzung an, holt den Nutzer und benachrichtigt', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledWith({ email: 'a@b.de', password: 'geheim123' });
|
|
expect(auth.currentUserId()).toBe('u1');
|
|
expect(listener).toHaveBeenCalledWith({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
});
|
|
|
|
it('bei falschem Passwort fliegt der Fehler weiter und nichts ändert sich', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
const error = fail(401, 'user_invalid_credentials');
|
|
mocks.createEmailPasswordSession.mockRejectedValue(error);
|
|
await expect(auth.login('a@b.de', 'falsch123')).rejects.toBe(error);
|
|
expect(mocks.get).not.toHaveBeenCalled();
|
|
expect(listener).not.toHaveBeenCalled();
|
|
expect(auth.currentUserId()).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('login bei bereits aktiver Sitzung (user_session_already_exists)', () => {
|
|
const existing = () => fail(401, 'user_session_already_exists', 'Creation of a session is prohibited when a session is active.');
|
|
|
|
it('löscht die aktuelle Sitzung, versucht es einmal neu und liefert den Nutzer', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
|
expect(mocks.deleteSession).toHaveBeenCalledWith({ sessionId: 'current' });
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenLastCalledWith({ email: 'a@b.de', password: 'geheim123' });
|
|
expect(auth.currentUserId()).toBe('u1');
|
|
});
|
|
|
|
it('ignoriert 401 beim Löschen (Sitzung war schon weg) und versucht es trotzdem neu', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
|
mocks.deleteSession.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de', labels: [] });
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('scheitert der zweite Versuch auch, fliegt dieser Fehler weiter (keine Endlosschleife)', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockRejectedValue(existing());
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await expect(auth.login('a@b.de', 'geheim123')).rejects.toMatchObject({ type: 'user_session_already_exists' });
|
|
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
|
expect(mocks.get).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('falsche Zugangsdaten im zweiten Versuch kommen als solche an, der alte Nutzer ist dann abgemeldet', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'old', email: 'old@b.de' });
|
|
await auth.getCurrentUser();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
const wrong = fail(401, 'user_invalid_credentials');
|
|
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockRejectedValueOnce(wrong);
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await expect(auth.login('a@b.de', 'falsch123')).rejects.toBe(wrong);
|
|
expect(auth.currentUserId()).toBeNull();
|
|
expect(listener).toHaveBeenCalledWith(null);
|
|
});
|
|
|
|
it('scheitert das Löschen anders (z. B. Netz), fliegt dieser Fehler weiter und es gibt keinen zweiten Versuch', async () => {
|
|
const auth = await freshAuth();
|
|
const error = new TypeError('Failed to fetch');
|
|
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing());
|
|
mocks.deleteSession.mockRejectedValue(error);
|
|
await expect(auth.login('a@b.de', 'geheim123')).rejects.toBe(error);
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('andere Fehler beim ersten Versuch löschen nichts', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockRejectedValue(fail(401, 'user_invalid_credentials'));
|
|
await expect(auth.login('a@b.de', 'falsch123')).rejects.toMatchObject({ type: 'user_invalid_credentials' });
|
|
expect(mocks.deleteSession).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('register: das anschließende Login räumt eine vorhandene Sitzung ebenfalls ab', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.create.mockResolvedValue({});
|
|
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u2', email: 'neu@b.de' });
|
|
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de', labels: [] });
|
|
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('register', () => {
|
|
it('legt das Konto an (Name aus der E-Mail) und meldet danach an', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.create.mockResolvedValue({});
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u2', email: 'neu@b.de' });
|
|
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de', labels: [] });
|
|
const params = mocks.create.mock.calls[0][0];
|
|
expect(params).toMatchObject({ email: 'neu@b.de', password: 'geheim123', name: 'neu' });
|
|
expect(typeof params.userId).toBe('string');
|
|
expect(params.userId.length).toBeGreaterThan(0);
|
|
expect(mocks.createEmailPasswordSession).toHaveBeenCalledWith({ email: 'neu@b.de', password: 'geheim123' });
|
|
expect(auth.currentUserId()).toBe('u2');
|
|
});
|
|
|
|
it('kürzt den Namen auf 128 Zeichen', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.create.mockResolvedValue({});
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u2', email: 'x@b.de' });
|
|
await auth.register(`${'a'.repeat(200)}@b.de`, 'geheim123');
|
|
expect(mocks.create.mock.calls[0][0].name).toBe('a'.repeat(128));
|
|
});
|
|
|
|
it('meldet nicht an, wenn das Konto nicht angelegt werden konnte', async () => {
|
|
const auth = await freshAuth();
|
|
const error = fail(409, 'user_already_exists');
|
|
mocks.create.mockRejectedValue(error);
|
|
await expect(auth.register('a@b.de', 'geheim123')).rejects.toBe(error);
|
|
expect(mocks.createEmailPasswordSession).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('logout', () => {
|
|
it('löscht die aktuelle Sitzung, leert den Cache und benachrichtigt', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.login('a@b.de', 'geheim123');
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await auth.logout();
|
|
expect(mocks.deleteSession).toHaveBeenCalledWith({ sessionId: 'current' });
|
|
expect(auth.currentUserId()).toBeNull();
|
|
expect(listener).toHaveBeenCalledWith(null);
|
|
});
|
|
|
|
it('401 (Sitzung war schon weg) löst auf und ruft onAuthChange(null)', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
auth.onAuthChange(listener);
|
|
mocks.deleteSession.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
|
await expect(auth.logout()).resolves.toBeUndefined();
|
|
expect(listener).toHaveBeenCalledWith(null);
|
|
expect(auth.currentUserId()).toBeNull();
|
|
});
|
|
|
|
it('andere Fehler (z. B. Netz) fliegen weiter, der Cache bleibt', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createEmailPasswordSession.mockResolvedValue({});
|
|
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.login('a@b.de', 'geheim123');
|
|
mocks.deleteSession.mockRejectedValue(new TypeError('Failed to fetch'));
|
|
await expect(auth.logout()).rejects.toBeInstanceOf(TypeError);
|
|
expect(auth.currentUserId()).toBe('u1');
|
|
});
|
|
});
|
|
|
|
describe('onLogout', () => {
|
|
it('wird nach dem eigenen Abmelden aufgerufen, auch wenn die Sitzung schon weg war (401)', async () => {
|
|
const auth = await freshAuth();
|
|
const fn = vi.fn();
|
|
auth.onLogout(fn);
|
|
mocks.deleteSession.mockResolvedValueOnce({});
|
|
await auth.logout();
|
|
expect(fn).toHaveBeenCalledTimes(1);
|
|
mocks.deleteSession.mockRejectedValueOnce(fail(401, 'general_unauthorized_scope'));
|
|
await auth.logout();
|
|
expect(fn).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('wird nicht aufgerufen, wenn nur eine abgelaufene Sitzung bemerkt wird (getCurrentUser → 401)', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.get.mockResolvedValueOnce({ $id: 'u1', email: 'a@b.de' });
|
|
await auth.getCurrentUser();
|
|
const fn = vi.fn();
|
|
const change = vi.fn();
|
|
auth.onLogout(fn);
|
|
auth.onAuthChange(change);
|
|
mocks.get.mockRejectedValueOnce(fail(401, 'general_unauthorized_scope'));
|
|
await auth.getCurrentUser();
|
|
expect(change).toHaveBeenCalledWith(null);
|
|
expect(fn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('wird nicht aufgerufen, wenn das Abmelden scheitert, und auch nicht beim Login mit alter Sitzung', async () => {
|
|
const auth = await freshAuth();
|
|
const fn = vi.fn();
|
|
auth.onLogout(fn);
|
|
mocks.deleteSession.mockRejectedValueOnce(new TypeError('Failed to fetch'));
|
|
await expect(auth.logout()).rejects.toBeInstanceOf(TypeError);
|
|
expect(fn).not.toHaveBeenCalled();
|
|
|
|
mocks.createEmailPasswordSession
|
|
.mockRejectedValueOnce(fail(409, 'user_session_already_exists'))
|
|
.mockResolvedValueOnce({});
|
|
mocks.deleteSession.mockResolvedValueOnce({});
|
|
mocks.get.mockResolvedValue({ $id: 'u2', email: 'b@b.de' });
|
|
await auth.login('b@b.de', 'geheim123');
|
|
expect(fn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('gibt eine Abmelde-Funktion zurück', async () => {
|
|
const auth = await freshAuth();
|
|
const fn = vi.fn();
|
|
auth.onLogout(fn)();
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await auth.logout();
|
|
expect(fn).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('onAuthChange', () => {
|
|
it('gibt eine Abmelde-Funktion zurück', async () => {
|
|
const auth = await freshAuth();
|
|
const listener = vi.fn();
|
|
const off = auth.onAuthChange(listener);
|
|
off();
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await auth.logout();
|
|
expect(listener).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('ein Abonnent darf sich beim Benachrichtigen abmelden', async () => {
|
|
const auth = await freshAuth();
|
|
const second = vi.fn();
|
|
const off = auth.onAuthChange(() => off());
|
|
auth.onAuthChange(second);
|
|
mocks.deleteSession.mockResolvedValue({});
|
|
await auth.logout();
|
|
expect(second).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('Passwort zurücksetzen', () => {
|
|
it('requestPasswordReset sendet die Adresse und die Rücksprung-URL', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.createRecovery.mockResolvedValue({});
|
|
await auth.requestPasswordReset('a@b.de');
|
|
expect(mocks.createRecovery).toHaveBeenCalledWith({ email: 'a@b.de', url: `${location.origin}/passwort-neu` });
|
|
});
|
|
|
|
it('completePasswordReset sendet userId, secret und neues Passwort', async () => {
|
|
const auth = await freshAuth();
|
|
mocks.updateRecovery.mockResolvedValue({});
|
|
await auth.completePasswordReset('u1', 'geheim-token', 'neuesPasswort1');
|
|
expect(mocks.updateRecovery).toHaveBeenCalledWith({ userId: 'u1', secret: 'geheim-token', password: 'neuesPasswort1' });
|
|
});
|
|
});
|
|
|
|
describe('validatePassword', () => {
|
|
it('lehnt zu kurze Passwörter mit einer Meldung ab', async () => {
|
|
const auth = await freshAuth();
|
|
expect(auth.validatePassword('kurz')).toBe('Das Passwort muss 8 bis 256 Zeichen lang sein.');
|
|
expect(auth.validatePassword('')).toBe('Das Passwort muss 8 bis 256 Zeichen lang sein.');
|
|
expect(auth.validatePassword('1234567')).not.toBeNull();
|
|
});
|
|
|
|
it('nimmt 8 bis 256 Zeichen an', async () => {
|
|
const auth = await freshAuth();
|
|
expect(auth.validatePassword('12345678')).toBeNull();
|
|
expect(auth.validatePassword('a'.repeat(256))).toBeNull();
|
|
});
|
|
|
|
it('lehnt mehr als 256 Zeichen ab', async () => {
|
|
const auth = await freshAuth();
|
|
expect(auth.validatePassword('a'.repeat(257))).toBe('Das Passwort muss 8 bis 256 Zeichen lang sein.');
|
|
});
|
|
});
|