import { describe, expect, it } from 'vitest' import { speicherfehlerErkennen, speicherfehlerMeldung } from '@/lib/speicher/fehler' /** * Speicherfehler müssen sagen, was zu tun ist. * * Anlass ist eine Fehlersuche vom 31.08.2026. In der Oberfläche stand * dreimal hintereinander derselbe Satz — „Das Schreiben konnte nicht * abgelegt werden." — und dahinter lagen drei völlig verschiedene * Ursachen: ein fehlender Ablagebereich, ein Endpunkt, der auf einen * fremden Anbieter zeigte, und eine Verschlüsselung, die der Speicher * nicht anbietet. * * Ohne Zugang zum Serverlog wäre die Suche aussichtslos gewesen. */ /** Baut einen Fehler, wie ihn das AWS-SDK wirft. */ function sdkFehler(code: string, nachricht = ''): Error { const fehler = new Error(nachricht || code) as Error & { Code: string } fehler.Code = code return fehler } describe('Speicherfehler erklären', () => { it('erkennt den fehlenden Ablagebereich und nennt den Installationsschritt', () => { const erkannt = speicherfehlerErkennen(sdkFehler('NoSuchBucket')) expect(erkannt?.art).toBe('kein-ablagebereich') expect(erkannt?.meldung).toMatch(/Schritt 6/) }) it('erkennt die nicht unterstützte Verschlüsselung und nennt die Stellschraube', () => { const erkannt = speicherfehlerErkennen( sdkFehler('NotImplemented', 'Server side encryption specified but KMS is not configured'), ) expect(erkannt?.art).toBe('verschluesselung') expect(erkannt?.meldung).toMatch(/S3_SSE/) }) it('verwechselt ein anderes NotImplemented nicht damit', () => { // "NotImplemented" schicken S3-Nachbauten fuer allerlei; nur der Fall // mit der Verschluesselung hat eine passende Erklaerung. expect(speicherfehlerErkennen(sdkFehler('NotImplemented', 'Versioning not supported'))).toBeNull() }) it.each(['AccessDenied', 'SignatureDoesNotMatch', 'InvalidAccessKeyId'])( 'erkennt %s als Zugangsproblem', (code) => { const erkannt = speicherfehlerErkennen(sdkFehler(code)) expect(erkannt?.art).toBe('zugang') expect(erkannt?.meldung).toMatch(/S3_ACCESS_KEY_ID/) }, ) it.each(['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT'])( 'erkennt %s als nicht erreichbar', (code) => { const fehler = new Error('connect failed') as Error & { code: string } fehler.code = code expect(speicherfehlerErkennen(fehler)?.art).toBe('nicht-erreichbar') }, ) it('verspricht bei einem Netzfehler, dass die Eingaben bleiben', () => { const fehler = new Error('connect ECONNREFUSED 172.18.0.3:9000') expect(speicherfehlerErkennen(fehler)?.meldung).toMatch(/gehen nicht verloren/) }) it('gibt bei einem unbekannten Fehler null zurück', () => { // Dann bleibt es bei der allgemeinen Meldung des Aufrufers. Fremde // Fehlertexte durchzureichen waere die schlechtere Wahl. expect(speicherfehlerErkennen(new Error('irgendwas ging schief'))).toBeNull() expect(speicherfehlerErkennen(null)).toBeNull() expect(speicherfehlerErkennen('kaputt')).toBeNull() }) it('verrät nichts über Hostnamen, Eimer oder Anfragekennungen', () => { // Solche Angaben stehen in den Fehlern des SDK und gehoeren ins Log, // nicht auf den Bildschirm eines Betreuers. const fehler = sdkFehler('NoSuchBucket', 'The specified bucket does not exist') as Error & { BucketName: string RequestId: string } fehler.BucketName = 'betreuungs-cockpit' fehler.RequestId = '18D0FEDD87C08296' const meldung = speicherfehlerErkennen(fehler)!.meldung expect(meldung).not.toContain('betreuungs-cockpit') expect(meldung).not.toContain('18D0FEDD87C08296') expect(meldung).not.toContain('bucket') }) }) describe('speicherfehlerMeldung', () => { it('nimmt die erkannte Erklärung, wenn es eine gibt', () => { const meldung = speicherfehlerMeldung(sdkFehler('NoSuchBucket'), 'Allgemeine Meldung.') expect(meldung).not.toBe('Allgemeine Meldung.') expect(meldung).toMatch(/Ablagebereich/) }) it('fällt sonst auf die Meldung des Aufrufers zurück', () => { const meldung = speicherfehlerMeldung(new Error('unbekannt'), 'Allgemeine Meldung.') expect(meldung).toBe('Allgemeine Meldung.') }) })