import Foundation import Testing @testable import ManaCore @Suite("AuthError.classify") struct AuthErrorClassifyTests { private func body(_ json: String) -> Data { Data(json.utf8) } @Test("Mapped INVALID_CREDENTIALS auf invalidCredentials") func mapsInvalidCredentials() { let err = AuthError.classify( status: 401, data: body(#"{"error":"INVALID_CREDENTIALS","message":"Email oder Passwort falsch","status":401}"#) ) #expect(err == .invalidCredentials) } @Test("Mapped EMAIL_NOT_VERIFIED auf emailNotVerified") func mapsEmailNotVerified() { let err = AuthError.classify( status: 403, data: body(#"{"error":"EMAIL_NOT_VERIFIED","message":"Bitte bestätige…","status":403}"#) ) #expect(err == .emailNotVerified) } @Test("Mapped EMAIL_ALREADY_REGISTERED auf emailAlreadyRegistered") func mapsEmailAlreadyRegistered() { let err = AuthError.classify( status: 409, data: body(#"{"error":"EMAIL_ALREADY_REGISTERED","status":409}"#) ) #expect(err == .emailAlreadyRegistered) } @Test("WEAK_PASSWORD trägt Server-Message") func weakPasswordCarriesMessage() { let err = AuthError.classify( status: 400, data: body(#"{"error":"WEAK_PASSWORD","message":"Mindestens 8 Zeichen","status":400}"#) ) if case let .weakPassword(message) = err { #expect(message == "Mindestens 8 Zeichen") } else { Issue.record("Expected .weakPassword, got \(err)") } } @Test("RATE_LIMITED nutzt retryAfterSec aus Body") func rateLimitedFromBody() { let err = AuthError.classify( status: 429, data: body(#"{"error":"RATE_LIMITED","retryAfterSec":30,"status":429}"#) ) if case let .rateLimited(retryAfter) = err { #expect(retryAfter == 30) } else { Issue.record("Expected .rateLimited(30), got \(err)") } } @Test("RATE_LIMITED fällt auf Retry-After-Header zurück") func rateLimitedFromHeader() { let err = AuthError.classify( status: 429, data: body(#"{"error":"RATE_LIMITED","status":429}"#), retryAfterHeader: 60 ) if case let .rateLimited(retryAfter) = err { #expect(retryAfter == 60) } else { Issue.record("Expected .rateLimited(60), got \(err)") } } @Test("ACCOUNT_LOCKED erhält retryAfter") func accountLockedRetryAfter() { let err = AuthError.classify( status: 423, data: body(#"{"error":"ACCOUNT_LOCKED","retryAfterSec":120,"status":423}"#) ) if case let .accountLocked(retryAfter) = err { #expect(retryAfter == 120) } else { Issue.record("Expected .accountLocked(120), got \(err)") } } @Test("SIGNUP_LIMIT_REACHED") func signupLimitReached() { let err = AuthError.classify( status: 429, data: body(#"{"error":"SIGNUP_LIMIT_REACHED","status":429}"#) ) #expect(err == .signupLimitReached) } @Test("TOKEN_EXPIRED und TOKEN_INVALID werden unterschieden") func tokenStates() { #expect( AuthError.classify(status: 400, data: body(#"{"error":"TOKEN_EXPIRED"}"#)) == .tokenExpired ) #expect( AuthError.classify(status: 400, data: body(#"{"error":"TOKEN_INVALID"}"#)) == .tokenInvalid ) } @Test("VALIDATION trägt Message") func validationCarriesMessage() { let err = AuthError.classify( status: 400, data: body(#"{"error":"VALIDATION","message":"email is required","status":400}"#) ) if case let .validation(message) = err { #expect(message == "email is required") } else { Issue.record("Expected .validation, got \(err)") } } @Test("Unbekannter Code mit Status 401 fällt auf invalidCredentials") func unknownCodeStatusHeuristic401() { let err = AuthError.classify( status: 401, data: body(#"{}"#) ) #expect(err == .invalidCredentials) } @Test("Unbekannter Code mit Status 503 fällt auf serviceUnavailable") func unknownCodeStatusHeuristic503() { let err = AuthError.classify( status: 503, data: body(#"{}"#) ) #expect(err == .serviceUnavailable) } @Test("Komplett kaputter Body führt zu serverError mit Status") func brokenBodyFallback() { let err = AuthError.classify( status: 500, data: body("nicht-json") ) if case let .serverError(status, code, _) = err { #expect(status == 500) #expect(code == nil) } else { Issue.record("Expected .serverError(500), got \(err)") } } @Test("errorDescription liefert deutsche Strings") func germanErrorDescriptions() { #expect(AuthError.invalidCredentials.errorDescription == "Email oder Passwort falsch") #expect(AuthError.emailAlreadyRegistered.errorDescription == "Diese Email ist bereits registriert.") #expect( AuthError.rateLimited(retryAfter: 30).errorDescription == "Zu viele Versuche. Bitte warte 30s." ) } }