Files
honeyDueKMP/iosApp/HoneyDueTests/ValidationRulesTests.swift
Trey T 334767cee7 Production hardening: password complexity, token refresh, network resilience
Password complexity: real-time validation UI on register, onboarding, and reset screens
  (uppercase, lowercase, digit, min 8 chars) — Compose + iOS Swift
iOS privacy descriptions: camera, photo library, photo save usage strings
Token refresh: Ktor interceptor catches 401 "token_expired", refreshes, retries
Retry with backoff: 3 retries on 5xx/IO errors, exponential delay (1s base, 10s max)
Gzip: ContentEncoding plugin on all platform HTTP clients
Request timeouts: 30s request, 10s connect, 30s socket
Validation rules: split passwordMissingLetter into uppercase/lowercase (iOS Swift)
Test fixes: corrected import paths in 5 existing test files
New tests: HTTP client retry/refresh (9), validation rules
2026-03-26 14:05:33 -05:00

298 lines
9.6 KiB
Swift

//
// ValidationRulesTests.swift
// honeyDueTests
//
// Unit tests for ValidationError and ValidationRules (distinct from ValidationHelpers).
//
import Testing
@testable import honeyDue
// MARK: - ValidationError errorDescription Tests
struct ValidationErrorTests {
@Test func requiredFieldErrorDescription() {
let error = ValidationError.required(field: "Email")
#expect(error.errorDescription == "Email is required")
}
@Test func invalidEmailErrorDescription() {
let error = ValidationError.invalidEmail
#expect(error.errorDescription == "Please enter a valid email address")
}
@Test func passwordTooShortErrorDescription() {
let error = ValidationError.passwordTooShort(minLength: 8)
#expect(error.errorDescription == "Password must be at least 8 characters")
}
@Test func passwordMismatchErrorDescription() {
let error = ValidationError.passwordMismatch
#expect(error.errorDescription == "Passwords do not match")
}
@Test func passwordMissingLetterErrorDescription() {
let error = ValidationError.passwordMissingLetter
#expect(error.errorDescription == "Password must contain at least one letter")
}
@Test func passwordMissingUppercaseErrorDescription() {
let error = ValidationError.passwordMissingUppercase
#expect(error.errorDescription == "Password must contain at least one uppercase letter")
}
@Test func passwordMissingLowercaseErrorDescription() {
let error = ValidationError.passwordMissingLowercase
#expect(error.errorDescription == "Password must contain at least one lowercase letter")
}
@Test func passwordMissingNumberErrorDescription() {
let error = ValidationError.passwordMissingNumber
#expect(error.errorDescription == "Password must contain at least one number")
}
@Test func invalidCodeErrorDescription() {
let error = ValidationError.invalidCode(expectedLength: 6)
#expect(error.errorDescription == "Code must be 6 digits")
}
@Test func invalidUsernameErrorDescription() {
let error = ValidationError.invalidUsername
#expect(error.errorDescription == "Username can only contain letters, numbers, and underscores")
}
@Test func customMessageErrorDescription() {
let error = ValidationError.custom(message: "Something went wrong")
#expect(error.errorDescription == "Something went wrong")
}
}
// MARK: - Email Validation Tests
struct ValidationRulesEmailTests {
@Test func emptyEmailReturnsRequired() {
let error = ValidationRules.validateEmail("")
#expect(error?.errorDescription == "Email is required")
}
@Test func whitespaceOnlyEmailReturnsRequired() {
let error = ValidationRules.validateEmail(" ")
#expect(error?.errorDescription == "Email is required")
}
@Test func invalidEmailReturnsInvalidEmail() {
let error = ValidationRules.validateEmail("notanemail")
#expect(error?.errorDescription == "Please enter a valid email address")
}
@Test func validEmailReturnsNil() {
let error = ValidationRules.validateEmail("user@example.com")
#expect(error == nil)
}
@Test func isValidEmailReturnsTrueForValid() {
#expect(ValidationRules.isValidEmail("user@example.com"))
}
@Test func isValidEmailReturnsFalseForInvalid() {
#expect(!ValidationRules.isValidEmail("notanemail"))
}
}
// MARK: - Password Validation Tests
struct ValidationRulesPasswordTests {
@Test func emptyPasswordReturnsRequired() {
let error = ValidationRules.validatePassword("")
#expect(error?.errorDescription == "Password is required")
}
@Test func shortPasswordReturnsTooShort() {
let error = ValidationRules.validatePassword("abc")
#expect(error?.errorDescription == "Password must be at least 8 characters")
}
@Test func validPasswordReturnsNil() {
let error = ValidationRules.validatePassword("longpassword")
#expect(error == nil)
}
@Test func exactMinLengthPasswordReturnsNil() {
let error = ValidationRules.validatePassword("12345678")
#expect(error == nil)
}
}
// MARK: - Password Strength Tests
struct ValidationRulesPasswordStrengthTests {
@Test func emptyPasswordReturnsRequired() {
let error = ValidationRules.validatePasswordStrength("")
#expect(error?.errorDescription == "Password is required")
}
@Test func noUppercaseReturnsMissingUppercase() {
let error = ValidationRules.validatePasswordStrength("abc123")
#expect(error?.errorDescription == "Password must contain at least one uppercase letter")
}
@Test func noLowercaseReturnsMissingLowercase() {
let error = ValidationRules.validatePasswordStrength("ABC123")
#expect(error?.errorDescription == "Password must contain at least one lowercase letter")
}
@Test func noNumberReturnsMissingNumber() {
let error = ValidationRules.validatePasswordStrength("Abcdef")
#expect(error?.errorDescription == "Password must contain at least one number")
}
@Test func uppercaseLowercaseAndNumberReturnsNil() {
let error = ValidationRules.validatePasswordStrength("Abc123")
#expect(error == nil)
}
@Test func isValidPasswordReturnsTrueForComplex() {
#expect(ValidationRules.isValidPassword("Abc123"))
}
@Test func isValidPasswordReturnsFalseForLowercaseOnly() {
#expect(!ValidationRules.isValidPassword("abcdef"))
}
@Test func isValidPasswordReturnsFalseForUppercaseOnly() {
#expect(!ValidationRules.isValidPassword("ABCDEF"))
}
@Test func isValidPasswordReturnsFalseForNumbersOnly() {
#expect(!ValidationRules.isValidPassword("123456"))
}
@Test func isValidPasswordReturnsFalseForNoUppercase() {
#expect(!ValidationRules.isValidPassword("abc123"))
}
@Test func isValidPasswordReturnsFalseForNoLowercase() {
#expect(!ValidationRules.isValidPassword("ABC123"))
}
@Test func isValidPasswordReturnsFalseForNoDigit() {
#expect(!ValidationRules.isValidPassword("Abcdef"))
}
}
// MARK: - Password Match Tests
struct ValidationRulesPasswordMatchTests {
@Test func matchingPasswordsReturnsNil() {
let error = ValidationRules.validatePasswordMatch("abc123", "abc123")
#expect(error == nil)
}
@Test func mismatchedPasswordsReturnsMismatch() {
let error = ValidationRules.validatePasswordMatch("abc123", "xyz789")
#expect(error?.errorDescription == "Passwords do not match")
}
@Test func caseSensitiveMismatch() {
let error = ValidationRules.validatePasswordMatch("Password", "password")
#expect(error?.errorDescription == "Passwords do not match")
}
}
// MARK: - Code Validation Tests
struct ValidationRulesCodeTests {
@Test func emptyCodeReturnsRequired() {
let error = ValidationRules.validateCode("")
#expect(error?.errorDescription == "Code is required")
}
@Test func wrongLengthReturnsInvalidCode() {
let error = ValidationRules.validateCode("123")
#expect(error?.errorDescription == "Code must be 6 digits")
}
@Test func nonNumericCodeReturnsInvalidCode() {
let error = ValidationRules.validateCode("abcdef")
#expect(error?.errorDescription == "Code must be 6 digits")
}
@Test func validSixDigitCodeReturnsNil() {
let error = ValidationRules.validateCode("123456")
#expect(error == nil)
}
@Test func customLengthFourDigitCode() {
let error = ValidationRules.validateCode("1234", expectedLength: 4)
#expect(error == nil)
}
@Test func customLengthWrongDigitCount() {
let error = ValidationRules.validateCode("123", expectedLength: 4)
#expect(error?.errorDescription == "Code must be 4 digits")
}
}
// MARK: - Username Validation Tests
struct ValidationRulesUsernameTests {
@Test func emptyUsernameReturnsRequired() {
let error = ValidationRules.validateUsername("")
#expect(error?.errorDescription == "Username is required")
}
@Test func validAlphanumericUsernameReturnsNil() {
let error = ValidationRules.validateUsername("john_doe123")
#expect(error == nil)
}
@Test func usernameWithSpacesReturnsInvalid() {
let error = ValidationRules.validateUsername("john doe")
#expect(error?.errorDescription == "Username can only contain letters, numbers, and underscores")
}
@Test func usernameWithSpecialCharsReturnsInvalid() {
let error = ValidationRules.validateUsername("user@name!")
#expect(error?.errorDescription == "Username can only contain letters, numbers, and underscores")
}
}
// MARK: - Required Field Validation Tests
struct ValidationRulesRequiredTests {
@Test func emptyValueReturnsRequired() {
let error = ValidationRules.validateRequired("", fieldName: "Name")
#expect(error?.errorDescription == "Name is required")
}
@Test func whitespaceOnlyReturnsRequired() {
let error = ValidationRules.validateRequired(" ", fieldName: "Name")
#expect(error?.errorDescription == "Name is required")
}
@Test func nonEmptyReturnsNil() {
let error = ValidationRules.validateRequired("hello", fieldName: "Name")
#expect(error == nil)
}
@Test func isNotEmptyReturnsTrueForNonEmpty() {
#expect(ValidationRules.isNotEmpty("hello"))
}
@Test func isNotEmptyReturnsFalseForEmpty() {
#expect(!ValidationRules.isNotEmpty(""))
}
@Test func isNotEmptyReturnsFalseForWhitespace() {
#expect(!ValidationRules.isNotEmpty(" "))
}
}