import Foundation import XCTest /// High-level account lifecycle management for UI tests. enum TestAccountManager { // MARK: - Credential Generation /// Generate unique credentials with a timestamp + random suffix to avoid collisions. static func uniqueCredentials(prefix: String = "uit") -> (username: String, email: String, password: String) { let stamp = Int(Date().timeIntervalSince1970) let random = Int.random(in: 1000...9999) let username = "\(prefix)_\(stamp)_\(random)" let email = "\(username)@test.example.com" let password = "Pass\(stamp)!" return (username, email, password) } // MARK: - Account Creation /// Create a verified account via the backend API. Returns a ready-to-use session. /// Calls `XCTFail` and returns nil if any step fails. static func createVerifiedAccount( file: StaticString = #filePath, line: UInt = #line ) -> TestSession? { let creds = uniqueCredentials() guard let session = TestAccountAPIClient.createVerifiedAccount( username: creds.username, email: creds.email, password: creds.password ) else { XCTFail("Failed to create verified account for \(creds.username)", file: file, line: line) return nil } return session } /// Create an unverified account (register only, no email verification). /// Useful for testing the verification gate. static func createUnverifiedAccount( file: StaticString = #filePath, line: UInt = #line ) -> TestSession? { let creds = uniqueCredentials() guard let response = TestAccountAPIClient.register( username: creds.username, email: creds.email, password: creds.password ) else { XCTFail("Failed to register unverified account for \(creds.username)", file: file, line: line) return nil } return TestSession( token: response.token, user: response.user, username: creds.username, password: creds.password ) } // MARK: - Seeded Accounts /// Login with a pre-seeded account that already exists in the database. static func loginSeededAccount( username: String = "admin", password: String = "Test1234", file: StaticString = #filePath, line: UInt = #line ) -> TestSession? { guard let response = TestAccountAPIClient.login(username: username, password: password) else { XCTFail("Failed to login seeded account '\(username)'", file: file, line: line) return nil } return TestSession( token: response.token, user: response.user, username: username, password: password ) } // MARK: - Password Reset /// Execute the full forgot→verify→reset cycle via the backend API. static func resetPassword( email: String, newPassword: String, file: StaticString = #filePath, line: UInt = #line ) -> Bool { guard TestAccountAPIClient.forgotPassword(email: email) != nil else { XCTFail("Forgot password request failed for \(email)", file: file, line: line) return false } guard let verifyResponse = TestAccountAPIClient.verifyResetCode(email: email) else { XCTFail("Verify reset code failed for \(email)", file: file, line: line) return false } guard TestAccountAPIClient.resetPassword(resetToken: verifyResponse.resetToken, newPassword: newPassword) != nil else { XCTFail("Reset password failed for \(email)", file: file, line: line) return false } return true } // MARK: - Token Management /// Invalidate a session token via the logout API. static func invalidateToken( _ session: TestSession, file: StaticString = #filePath, line: UInt = #line ) { if TestAccountAPIClient.logout(token: session.token) == nil { XCTFail("Failed to invalidate token for \(session.username)", file: file, line: line) } } }