Files
honeyDueKMP/iosApp/CaseraTests/PasswordResetViewModelTests.swift
treyt 0c803af9bc Add 4 new unit test suites for greenfield test plan coverage
Add 87 new tests (384 total) covering ValidationRules/ValidationError,
PasswordResetViewModel navigation and client-side validation, WidgetAction
Codable/Equatable/accessors, parseDate, and ThemeID enum properties.
Updates greenfield CSV for AUTH-012/017/018, NOTIF-006/007, WID-003, THEME-002.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 19:29:12 -06:00

219 lines
6.3 KiB
Swift

//
// 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.")
}
}