stack/sdks/implementations/swift/Tests/StackAuthTests/ErrorTests.swift
Konstantin Wohlwend 66b066db6e Swift SDK
2026-01-19 13:14:13 -08:00

249 lines
8.7 KiB
Swift

import Testing
import Foundation
@testable import StackAuth
@Suite("Error Handling Tests")
struct ErrorHandlingTests {
// MARK: - Authentication Errors
@Test("Should throw EmailPasswordMismatchError for wrong credentials")
func emailPasswordMismatchError() async throws {
let app = TestConfig.createClientApp()
do {
try await app.signInWithCredential(email: "nonexistent@example.com", password: "wrong")
Issue.record("Expected EmailPasswordMismatchError")
} catch is EmailPasswordMismatchError {
// Expected
} catch let error as StackAuthErrorProtocol where error.code == "EMAIL_PASSWORD_MISMATCH" {
// Also acceptable
}
}
@Test("Should throw UserWithEmailAlreadyExistsError for duplicate sign up")
func userAlreadyExistsError() async throws {
let app = TestConfig.createClientApp()
let email = TestConfig.uniqueEmail()
try await app.signUpWithCredential(email: email, password: TestConfig.testPassword)
try await app.signOut()
do {
try await app.signUpWithCredential(email: email, password: TestConfig.testPassword)
Issue.record("Expected UserWithEmailAlreadyExistsError")
} catch is UserWithEmailAlreadyExistsError {
// Expected
} catch let error as StackAuthErrorProtocol where error.code == "USER_EMAIL_ALREADY_EXISTS" {
// Also acceptable
}
}
@Test("Should throw UserNotSignedInError for unauthenticated access")
func userNotSignedInError() async throws {
let app = TestConfig.createClientApp()
await #expect(throws: UserNotSignedInError.self) {
_ = try await app.getUser(or: .throw)
}
}
// MARK: - Error Properties
@Test("Should include error code in error")
func errorIncludesCode() async throws {
let app = TestConfig.createClientApp()
do {
try await app.signInWithCredential(email: "nonexistent@example.com", password: "wrong")
Issue.record("Expected error")
} catch let error as StackAuthErrorProtocol {
#expect(!error.code.isEmpty)
#expect(error.code == "EMAIL_PASSWORD_MISMATCH")
}
}
@Test("Should include error message in error")
func errorIncludesMessage() async throws {
let app = TestConfig.createClientApp()
do {
try await app.signInWithCredential(email: "nonexistent@example.com", password: "wrong")
Issue.record("Expected error")
} catch let error as StackAuthErrorProtocol {
#expect(!error.message.isEmpty)
}
}
@Test("Should have meaningful error description")
func errorHasMeaningfulDescription() async throws {
let app = TestConfig.createClientApp()
do {
try await app.signInWithCredential(email: "nonexistent@example.com", password: "wrong")
Issue.record("Expected error")
} catch let error as StackAuthErrorProtocol {
let description = error.description
#expect(!description.isEmpty)
#expect(description.contains("EMAIL_PASSWORD_MISMATCH") || description.contains("password"))
}
}
// MARK: - Error Type Matching
@Test("Should match StackAuthError for unknown error codes")
func unknownErrorCodeMatchesStackAuthError() async throws {
// Create a StackAuthError with unknown code
let error = StackAuthError(code: "UNKNOWN_ERROR_CODE", message: "Test error")
#expect(error.code == "UNKNOWN_ERROR_CODE")
#expect(error.message == "Test error")
}
@Test("Should properly identify specific error types")
func identifySpecificErrorTypes() async throws {
let emailError = EmailPasswordMismatchError()
let userExistsError = UserWithEmailAlreadyExistsError()
let notSignedInError = UserNotSignedInError()
#expect(emailError.code == "EMAIL_PASSWORD_MISMATCH")
#expect(userExistsError.code == "USER_EMAIL_ALREADY_EXISTS")
#expect(notSignedInError.code == "USER_NOT_SIGNED_IN")
}
// MARK: - Error Recovery
@Test("Should be able to retry after authentication error")
func retryAfterAuthError() async throws {
let app = TestConfig.createClientApp()
let email = TestConfig.uniqueEmail()
// Sign up
try await app.signUpWithCredential(email: email, password: TestConfig.testPassword)
try await app.signOut()
// First try with wrong password
do {
try await app.signInWithCredential(email: email, password: "WrongPassword123!")
} catch is EmailPasswordMismatchError {
// Expected
}
// Should still be able to sign in with correct password
try await app.signInWithCredential(email: email, password: TestConfig.testPassword)
let user = try await app.getUser()
#expect(user != nil)
}
// MARK: - Server-Side Errors
@Test("Should handle user not found for server operations")
func serverUserNotFound() async throws {
let app = TestConfig.createServerApp()
let fakeUserId = UUID().uuidString
let user = try await app.getUser(id: fakeUserId)
// Should return nil, not throw
#expect(user == nil)
}
@Test("Should handle team not found for server operations")
func serverTeamNotFound() async throws {
let app = TestConfig.createServerApp()
let fakeTeamId = UUID().uuidString
let team = try await app.getTeam(id: fakeTeamId)
// Should return nil, not throw
#expect(team == nil)
}
// MARK: - Password Errors
@Test("Should throw for weak password")
func weakPasswordError() async throws {
let app = TestConfig.createClientApp()
let email = TestConfig.uniqueEmail()
do {
try await app.signUpWithCredential(email: email, password: "123")
Issue.record("Expected password error")
} catch is PasswordRequirementsNotMetError {
// Expected
} catch let error as StackAuthErrorProtocol where error.code == "PASSWORD_REQUIREMENTS_NOT_MET" || error.code == "PASSWORD_TOO_SHORT" {
// Also acceptable - different error codes for password issues
}
}
@Test("Should throw PasswordConfirmationMismatchError for wrong old password")
func wrongOldPasswordError() async throws {
let app = TestConfig.createClientApp()
let email = TestConfig.uniqueEmail()
try await app.signUpWithCredential(email: email, password: TestConfig.testPassword)
let user = try await app.getUser()
do {
try await user?.updatePassword(oldPassword: "WrongOld123!", newPassword: "NewPass456!")
Issue.record("Expected PasswordConfirmationMismatchError")
} catch is PasswordConfirmationMismatchError {
// Expected
} catch let error as StackAuthErrorProtocol where error.code == "PASSWORD_CONFIRMATION_MISMATCH" {
// Also acceptable
}
}
}
@Suite("Project Tests")
struct ProjectTests {
// MARK: - Project Info Tests
@Test("Should get project info via client")
func getProjectViaClient() async throws {
let app = TestConfig.createClientApp()
let project = try await app.getProject()
#expect(project.id == testProjectId)
}
@Test("Should get project info via server")
func getProjectViaServer() async throws {
let app = TestConfig.createServerApp()
let project = try await app.getProject()
#expect(project.id == testProjectId)
}
@Test("Should access project config")
func accessProjectConfig() async throws {
let app = TestConfig.createClientApp()
let project = try await app.getProject()
// Config should exist (even if empty)
let _ = project.config
}
@Test("Should create client app with correct project ID")
func createClientAppWithProjectId() async throws {
let app = TestConfig.createClientApp()
let projectId = await app.projectId
#expect(projectId == testProjectId)
}
@Test("Should create server app with correct project ID")
func createServerAppWithProjectId() async throws {
let app = TestConfig.createServerApp()
let projectId = await app.projectId
#expect(projectId == testProjectId)
}
}