// // PasswordResetViewModelTests.swift // CaseraTests // // Unit tests for PasswordResetViewModel navigation, state management, // and client-side validation (no network calls). // import Testing @testable import Casera // MARK: - PasswordResetStep Tests struct PasswordResetStepTests { @Test func allCasesHasFiveValues() { #expect(PasswordResetStep.allCases.count == 5) } @Test func allCasesContainsExpectedSteps() { let cases = PasswordResetStep.allCases #expect(cases.contains(.requestCode)) #expect(cases.contains(.verifyCode)) #expect(cases.contains(.resetPassword)) #expect(cases.contains(.loggingIn)) #expect(cases.contains(.success)) } } // MARK: - Initialization Tests @MainActor struct PasswordResetViewModelInitTests { @Test func defaultInitStartsAtRequestCode() { let vm = PasswordResetViewModel() #expect(vm.currentStep == .requestCode) #expect(vm.resetToken == nil) } @Test func initWithTokenStartsAtResetPassword() { let vm = PasswordResetViewModel(resetToken: "abc123") #expect(vm.currentStep == .resetPassword) #expect(vm.resetToken == "abc123") } } // MARK: - Navigation Tests @MainActor struct PasswordResetViewModelNavigationTests { @Test func moveToNextStepFromRequestCode() { let vm = PasswordResetViewModel() vm.currentStep = .requestCode vm.moveToNextStep() #expect(vm.currentStep == .verifyCode) } @Test func moveToNextStepFromVerifyCode() { let vm = PasswordResetViewModel() vm.currentStep = .verifyCode vm.moveToNextStep() #expect(vm.currentStep == .resetPassword) } @Test func moveToNextStepFromResetPassword() { let vm = PasswordResetViewModel() vm.currentStep = .resetPassword vm.moveToNextStep() #expect(vm.currentStep == .loggingIn) } @Test func moveToNextStepFromLoggingIn() { let vm = PasswordResetViewModel() vm.currentStep = .loggingIn vm.moveToNextStep() #expect(vm.currentStep == .success) } @Test func moveToNextStepFromSuccessIsNoOp() { let vm = PasswordResetViewModel() vm.currentStep = .success vm.moveToNextStep() #expect(vm.currentStep == .success) } @Test func moveToPreviousStepFromVerifyCode() { let vm = PasswordResetViewModel() vm.currentStep = .verifyCode vm.moveToPreviousStep() #expect(vm.currentStep == .requestCode) } @Test func moveToPreviousStepFromResetPassword() { let vm = PasswordResetViewModel() vm.currentStep = .resetPassword vm.moveToPreviousStep() #expect(vm.currentStep == .verifyCode) } @Test func moveToPreviousStepFromRequestCodeIsNoOp() { let vm = PasswordResetViewModel() vm.currentStep = .requestCode vm.moveToPreviousStep() #expect(vm.currentStep == .requestCode) } @Test func moveToPreviousStepFromLoggingInIsNoOp() { let vm = PasswordResetViewModel() vm.currentStep = .loggingIn vm.moveToPreviousStep() #expect(vm.currentStep == .loggingIn) } @Test func moveToPreviousStepFromSuccessIsNoOp() { let vm = PasswordResetViewModel() vm.currentStep = .success vm.moveToPreviousStep() #expect(vm.currentStep == .success) } } // MARK: - Reset and Clear Tests @MainActor struct PasswordResetViewModelResetTests { @Test func resetClearsAllState() { let vm = PasswordResetViewModel() vm.email = "test@example.com" vm.code = "123456" vm.newPassword = "password123" vm.confirmPassword = "password123" vm.resetToken = "token" vm.errorMessage = "error" vm.successMessage = "success" vm.currentStep = .resetPassword vm.isLoading = true vm.reset() #expect(vm.email == "") #expect(vm.code == "") #expect(vm.newPassword == "") #expect(vm.confirmPassword == "") #expect(vm.resetToken == nil) #expect(vm.errorMessage == nil) #expect(vm.successMessage == nil) #expect(vm.currentStep == .requestCode) #expect(vm.isLoading == false) } @Test func clearErrorNilsOutErrorMessage() { let vm = PasswordResetViewModel() vm.errorMessage = "Something went wrong" vm.clearError() #expect(vm.errorMessage == nil) } @Test func clearSuccessNilsOutSuccessMessage() { let vm = PasswordResetViewModel() vm.successMessage = "Code sent!" vm.clearSuccess() #expect(vm.successMessage == nil) } } // MARK: - Client-Side Validation Tests (verifyResetCode / resetPassword gates) @MainActor struct PasswordResetViewModelValidationTests { @Test func verifyResetCodeWithEmptyCodeSetsError() { let vm = PasswordResetViewModel() vm.code = "" vm.verifyResetCode() #expect(vm.errorMessage == "Code is required") } @Test func verifyResetCodeWithNonNumericCodeSetsError() { let vm = PasswordResetViewModel() vm.code = "abcdef" vm.verifyResetCode() #expect(vm.errorMessage == "Code must be 6 digits") } @Test func resetPasswordWithEmptyPasswordSetsError() { let vm = PasswordResetViewModel() vm.newPassword = "" vm.resetPassword() #expect(vm.errorMessage == "Password is required") } @Test func resetPasswordWithWeakPasswordSetsError() { let vm = PasswordResetViewModel() vm.newPassword = "abcdefgh" // no number vm.resetPassword() #expect(vm.errorMessage == "Password must contain at least one number") } @Test func resetPasswordWithMismatchedPasswordsSetsError() { let vm = PasswordResetViewModel() vm.newPassword = "abc12345" vm.confirmPassword = "xyz12345" vm.resetPassword() #expect(vm.errorMessage == "Passwords do not match") } @Test func resetPasswordWithNilTokenSetsError() { let vm = PasswordResetViewModel() vm.newPassword = "abc12345" vm.confirmPassword = "abc12345" vm.resetToken = nil vm.resetPassword() #expect(vm.errorMessage == "Invalid reset token. Please start over.") } }