194 lines
8.0 KiB
TypeScript
194 lines
8.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { errorMessage, errorType, isInvalidLinkError, isRowMissing, isUnauthorized, type ErrorContext } from './errors';
|
||
|
||
/** Fehler wie vom SDK (AppwriteException): Felder code, type, message. */
|
||
const fail = (code: number, type: string, message = '') => ({ code, type, message });
|
||
|
||
const LINK = 'Der Link ist ungültig oder abgelaufen.';
|
||
const WRONG = 'E-Mail oder Passwort ist falsch.';
|
||
const LENGTH = 'Das Passwort muss 8 bis 256 Zeichen lang sein.';
|
||
const COMMON = 'Dieses Passwort ist zu verbreitet. Bitte wähle ein anderes.';
|
||
const EMAIL = 'Bitte gib eine gültige E-Mail-Adresse ein.';
|
||
const GENERIC = 'Etwas ist schiefgelaufen. Bitte nochmal versuchen.';
|
||
|
||
const PW_LENGTH = 'Invalid `password` param: Password must be between 8 and 256 characters long.';
|
||
const PW_COMMON =
|
||
'Invalid `password` param: Password must be at least 8 characters and should not be one of the commonly used password.';
|
||
const EMAIL_INVALID = 'Invalid `email` param: Value must be a valid email address';
|
||
|
||
describe('errorMessage nach type', () => {
|
||
it('user_invalid_credentials', () => {
|
||
expect(errorMessage(fail(401, 'user_invalid_credentials', 'Invalid credentials.'), 'login')).toBe(WRONG);
|
||
});
|
||
|
||
it('user_already_exists', () => {
|
||
expect(errorMessage(fail(409, 'user_already_exists'), 'register')).toBe(
|
||
'Diese E-Mail ist schon registriert. Melde dich an.',
|
||
);
|
||
});
|
||
|
||
it('user_invalid_token', () => {
|
||
expect(errorMessage(fail(401, 'user_invalid_token'), 'reset')).toBe(LINK);
|
||
});
|
||
|
||
it('user_not_found hängt vom Kontext ab', () => {
|
||
expect(errorMessage(fail(404, 'user_not_found'), 'recover')).toBe('Zu dieser E-Mail gibt es kein Konto.');
|
||
expect(errorMessage(fail(404, 'user_not_found'), 'reset')).toBe(LINK);
|
||
});
|
||
|
||
it('user_not_found in anderen Kontexten fällt auf die allgemeine Meldung zurück', () => {
|
||
expect(errorMessage(fail(404, 'user_not_found'), 'save')).toBe(GENERIC);
|
||
});
|
||
|
||
it('row_not_found', () => {
|
||
expect(errorMessage(fail(404, 'row_not_found'), 'load')).toBe(
|
||
'Diese Karte gibt es nicht (mehr) oder sie gehört nicht zu deinem Konto.',
|
||
);
|
||
});
|
||
|
||
it('storage_file_not_found', () => {
|
||
expect(errorMessage(fail(404, 'storage_file_not_found'), 'load')).toBe('Das Bild dieser Karte fehlt.');
|
||
});
|
||
|
||
it('general_unknown_origin', () => {
|
||
expect(errorMessage(fail(403, 'general_unknown_origin'), 'login')).toBe(
|
||
'Diese Seite ist beim Server nicht freigeschaltet. Bitte melde dich beim Betreiber.',
|
||
);
|
||
});
|
||
|
||
it('user_session_already_exists: Hinweis mit Handlungsanweisung', () => {
|
||
expect(errorMessage(fail(401, 'user_session_already_exists'), 'login')).toBe(
|
||
'Du bist schon angemeldet. Bitte lade die Seite neu.',
|
||
);
|
||
expect(errorMessage(fail(401, 'user_session_already_exists'), 'register')).toBe(
|
||
'Du bist schon angemeldet. Bitte lade die Seite neu.',
|
||
);
|
||
});
|
||
|
||
it('user_count_exceeded', () => {
|
||
expect(errorMessage(fail(501, 'user_count_exceeded'), 'register')).toBe('Zurzeit sind keine neuen Konten möglich.');
|
||
});
|
||
});
|
||
|
||
describe('errorMessage bei general_argument_invalid', () => {
|
||
const invalid = (message: string) => fail(400, 'general_argument_invalid', message);
|
||
|
||
it('email', () => {
|
||
for (const ctx of ['login', 'register', 'recover'] as ErrorContext[]) {
|
||
expect(errorMessage(invalid(EMAIL_INVALID), ctx)).toBe(EMAIL);
|
||
}
|
||
});
|
||
|
||
it('password beim Login heißt: falsche Zugangsdaten', () => {
|
||
expect(errorMessage(invalid(PW_LENGTH), 'login')).toBe(WRONG);
|
||
});
|
||
|
||
it('password sonst: 8 bis 256 Zeichen', () => {
|
||
expect(errorMessage(invalid(PW_LENGTH), 'register')).toBe(LENGTH);
|
||
expect(errorMessage(invalid(PW_LENGTH), 'reset')).toBe(LENGTH);
|
||
});
|
||
|
||
it('password sonst: zu verbreitet', () => {
|
||
expect(errorMessage(invalid(PW_COMMON), 'register')).toBe(COMMON);
|
||
expect(errorMessage(invalid(PW_COMMON), 'reset')).toBe(COMMON);
|
||
});
|
||
|
||
it('userId und secret: Link ungültig', () => {
|
||
expect(errorMessage(invalid('Invalid `userId` param: Parameter must be a valid number'), 'reset')).toBe(LINK);
|
||
expect(errorMessage(invalid('Invalid `secret` param: Value must be a valid string'), 'reset')).toBe(LINK);
|
||
});
|
||
|
||
it('unbekannter oder fehlender Parameter: allgemeine Meldung', () => {
|
||
expect(errorMessage(invalid('Invalid `url` param: Invalid URI'), 'recover')).toBe(GENERIC);
|
||
expect(errorMessage(invalid('Something is wrong'), 'register')).toBe(GENERIC);
|
||
});
|
||
});
|
||
|
||
describe('errorMessage nach code und Fehlerart', () => {
|
||
it('429', () => {
|
||
expect(errorMessage(fail(429, 'general_rate_limit_exceeded'), 'login')).toBe('Zu viele Versuche – bitte kurz warten.');
|
||
expect(errorMessage(fail(429, ''), 'recover')).toBe('Zu viele Versuche – bitte kurz warten.');
|
||
});
|
||
|
||
it('type geht vor code', () => {
|
||
expect(errorMessage(fail(429, 'user_invalid_credentials'), 'login')).toBe(WRONG);
|
||
});
|
||
|
||
it('TypeError ist ein Netzfehler', () => {
|
||
expect(errorMessage(new TypeError('Failed to fetch'), 'save')).toBe('Keine Verbindung zum Server.');
|
||
});
|
||
|
||
it('alles andere ist die allgemeine Meldung', () => {
|
||
expect(errorMessage(fail(500, 'general_server_error', 'boom'), 'save')).toBe(GENERIC);
|
||
expect(errorMessage(new Error('x'), 'load')).toBe(GENERIC);
|
||
expect(errorMessage(undefined, 'load')).toBe(GENERIC);
|
||
expect(errorMessage('text', 'load')).toBe(GENERIC);
|
||
expect(errorMessage(null, 'load')).toBe(GENERIC);
|
||
});
|
||
});
|
||
|
||
describe('isUnauthorized', () => {
|
||
it('erkennt Code 401', () => {
|
||
expect(isUnauthorized({ code: 401 })).toBe(true);
|
||
});
|
||
|
||
it('andere Codes und Nicht-Fehler nicht', () => {
|
||
expect(isUnauthorized({ code: 404 })).toBe(false);
|
||
expect(isUnauthorized(new TypeError('Failed to fetch'))).toBe(false);
|
||
expect(isUnauthorized(null)).toBe(false);
|
||
expect(isUnauthorized(undefined)).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('errorType', () => {
|
||
it('liefert den type-String oder null', () => {
|
||
expect(errorType(fail(409, 'user_already_exists'))).toBe('user_already_exists');
|
||
expect(errorType(fail(500, ''))).toBeNull();
|
||
expect(errorType(new TypeError('x'))).toBeNull();
|
||
expect(errorType(undefined)).toBeNull();
|
||
});
|
||
});
|
||
|
||
describe('isRowMissing', () => {
|
||
it('404 mit row_not_found heißt: die Zeile gibt es nicht (oder nicht für mich)', () => {
|
||
expect(isRowMissing(fail(404, 'row_not_found'))).toBe(true);
|
||
});
|
||
|
||
it('404 ohne Typ (leer oder fehlend) zählt ebenso', () => {
|
||
expect(isRowMissing(fail(404, ''))).toBe(true);
|
||
expect(isRowMissing({ code: 404 })).toBe(true);
|
||
});
|
||
|
||
it('eine fehlende Tabelle, Datenbank oder Datei ist ein echter Fehler, kein „Zeile weg“', () => {
|
||
for (const type of ['table_not_found', 'database_not_found', 'storage_file_not_found']) {
|
||
expect(isRowMissing(fail(404, type)), type).toBe(false);
|
||
}
|
||
});
|
||
|
||
it('andere Status und Nicht-Fehler nicht', () => {
|
||
expect(isRowMissing(fail(401, 'row_not_found'))).toBe(false);
|
||
expect(isRowMissing(fail(500, 'row_not_found'))).toBe(false);
|
||
expect(isRowMissing({ code: '404', type: 'row_not_found' })).toBe(false);
|
||
expect(isRowMissing(new TypeError('Failed to fetch'))).toBe(false);
|
||
expect(isRowMissing('row_not_found')).toBe(false);
|
||
expect(isRowMissing(null)).toBe(false);
|
||
expect(isRowMissing(undefined)).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('isInvalidLinkError (Passwort-neu-Seite)', () => {
|
||
it('Link-Fehler', () => {
|
||
expect(isInvalidLinkError(fail(401, 'user_invalid_token'))).toBe(true);
|
||
expect(isInvalidLinkError(fail(404, 'user_not_found'))).toBe(true);
|
||
expect(isInvalidLinkError(fail(400, 'general_argument_invalid', 'Invalid `userId` param: x'))).toBe(true);
|
||
expect(isInvalidLinkError(fail(400, 'general_argument_invalid', 'Invalid `secret` param: x'))).toBe(true);
|
||
});
|
||
|
||
it('Passwort- und sonstige Fehler gehören zum Feld', () => {
|
||
expect(isInvalidLinkError(fail(400, 'general_argument_invalid', PW_LENGTH))).toBe(false);
|
||
expect(isInvalidLinkError(fail(429, 'general_rate_limit_exceeded'))).toBe(false);
|
||
expect(isInvalidLinkError(new TypeError('Failed to fetch'))).toBe(false);
|
||
expect(isInvalidLinkError(undefined)).toBe(false);
|
||
});
|
||
});
|