Total rebrand across KMM project: - Kotlin package: com.example.casera -> com.tt.honeyDue (dirs + declarations) - Gradle: rootProject.name, namespace, applicationId - Android: manifest, strings.xml (all languages), widget resources - iOS: pbxproj bundle IDs, Info.plist, entitlements, xcconfig - iOS directories: Casera/ -> HoneyDue/, CaseraTests/ -> HoneyDueTests/, etc. - Swift source: all class/struct/enum renames - Deep links: casera:// -> honeydue://, .casera -> .honeydue - App icons replaced with honeyDue honeycomb icon - Domains: casera.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - Database table names preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
446 lines
13 KiB
Swift
446 lines
13 KiB
Swift
//
|
|
// ValidationHelpersTests.swift
|
|
// honeyDueTests
|
|
//
|
|
// Unit tests for ValidationHelpers, FormValidator, and related types.
|
|
//
|
|
|
|
import Testing
|
|
@testable import honeyDue
|
|
|
|
// MARK: - ValidationResult Tests
|
|
|
|
struct ValidationResultTests {
|
|
|
|
@Test func validResultIsValid() {
|
|
let result = ValidationResult.valid
|
|
#expect(result.isValid)
|
|
#expect(result.errorMessage == nil)
|
|
}
|
|
|
|
@Test func invalidResultIsNotValid() {
|
|
let result = ValidationResult.invalid("Error")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Error")
|
|
}
|
|
}
|
|
|
|
// MARK: - Email Validation Tests
|
|
|
|
struct EmailValidationTests {
|
|
|
|
@Test func validEmailPasses() {
|
|
let result = ValidationHelpers.validateEmail("user@example.com")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyEmailFails() {
|
|
let result = ValidationHelpers.validateEmail("")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Email is required")
|
|
}
|
|
|
|
@Test func whitespaceOnlyEmailFails() {
|
|
let result = ValidationHelpers.validateEmail(" ")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Email is required")
|
|
}
|
|
|
|
@Test func emailWithoutAtSignFails() {
|
|
let result = ValidationHelpers.validateEmail("userexample.com")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Please enter a valid email address")
|
|
}
|
|
|
|
@Test func emailWithoutDomainFails() {
|
|
let result = ValidationHelpers.validateEmail("user@")
|
|
#expect(!result.isValid)
|
|
}
|
|
|
|
@Test func emailWithSubdomainPasses() {
|
|
let result = ValidationHelpers.validateEmail("user@mail.example.com")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emailWithPlusAddressingPasses() {
|
|
let result = ValidationHelpers.validateEmail("user+tag@example.com")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emailWithDotsInLocalPartPasses() {
|
|
let result = ValidationHelpers.validateEmail("first.last@example.com")
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Password Validation Tests
|
|
|
|
struct PasswordValidationTests {
|
|
|
|
@Test func validPasswordPasses() {
|
|
let result = ValidationHelpers.validatePassword("StrongPass1")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyPasswordFails() {
|
|
let result = ValidationHelpers.validatePassword("")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Password is required")
|
|
}
|
|
|
|
@Test func shortPasswordFails() {
|
|
let result = ValidationHelpers.validatePassword("short")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Password must be at least 8 characters")
|
|
}
|
|
|
|
@Test func exactMinLengthPasswordPasses() {
|
|
let result = ValidationHelpers.validatePassword("12345678")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func customMinLengthEnforced() {
|
|
let result = ValidationHelpers.validatePassword("1234", minLength: 6)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Password must be at least 6 characters")
|
|
}
|
|
|
|
@Test func customMinLengthPassesWhenMet() {
|
|
let result = ValidationHelpers.validatePassword("123456", minLength: 6)
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Password Confirmation Tests
|
|
|
|
struct PasswordConfirmationTests {
|
|
|
|
@Test func matchingPasswordsPasses() {
|
|
let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "password123")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func mismatchedPasswordsFails() {
|
|
let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "password456")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Passwords do not match")
|
|
}
|
|
|
|
@Test func emptyConfirmationFails() {
|
|
let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "")
|
|
#expect(!result.isValid)
|
|
}
|
|
|
|
@Test func caseSensitiveMismatchFails() {
|
|
let result = ValidationHelpers.validatePasswordConfirmation("Password", confirmation: "password")
|
|
#expect(!result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Name Validation Tests
|
|
|
|
struct NameValidationTests {
|
|
|
|
@Test func validNamePasses() {
|
|
let result = ValidationHelpers.validateName("John")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyNameFails() {
|
|
let result = ValidationHelpers.validateName("")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Name is required")
|
|
}
|
|
|
|
@Test func singleCharNameFails() {
|
|
let result = ValidationHelpers.validateName("J")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Name must be at least 2 characters")
|
|
}
|
|
|
|
@Test func twoCharNamePasses() {
|
|
let result = ValidationHelpers.validateName("Jo")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func customFieldNameInError() {
|
|
let result = ValidationHelpers.validateName("", fieldName: "Username")
|
|
#expect(result.errorMessage == "Username is required")
|
|
}
|
|
|
|
@Test func whitespaceOnlyNameFails() {
|
|
let result = ValidationHelpers.validateName(" ")
|
|
#expect(!result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Phone Validation Tests
|
|
|
|
struct PhoneValidationTests {
|
|
|
|
@Test func validUSPhonePasses() {
|
|
let result = ValidationHelpers.validatePhone("(555) 123-4567")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyPhoneFails() {
|
|
let result = ValidationHelpers.validatePhone("")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Phone number is required")
|
|
}
|
|
|
|
@Test func shortPhoneFails() {
|
|
let result = ValidationHelpers.validatePhone("12345")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Please enter a valid phone number")
|
|
}
|
|
|
|
@Test func phoneWithCountryCodePasses() {
|
|
let result = ValidationHelpers.validatePhone("+1 555-123-4567")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func digitsOnlyPhonePasses() {
|
|
let result = ValidationHelpers.validatePhone("5551234567")
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Required Field Validation Tests
|
|
|
|
struct RequiredFieldValidationTests {
|
|
|
|
@Test func nonEmptyPasses() {
|
|
let result = ValidationHelpers.validateRequired("value", fieldName: "Field")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyFails() {
|
|
let result = ValidationHelpers.validateRequired("", fieldName: "Title")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Title is required")
|
|
}
|
|
|
|
@Test func whitespaceOnlyFails() {
|
|
let result = ValidationHelpers.validateRequired(" ", fieldName: "Description")
|
|
#expect(!result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Number Validation Tests
|
|
|
|
struct NumberValidationTests {
|
|
|
|
@Test func validNumberPasses() {
|
|
let result = ValidationHelpers.validateNumber("42.5", fieldName: "Cost")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyNumberFails() {
|
|
let result = ValidationHelpers.validateNumber("", fieldName: "Cost")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Cost is required")
|
|
}
|
|
|
|
@Test func nonNumericFails() {
|
|
let result = ValidationHelpers.validateNumber("abc", fieldName: "Cost")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Cost must be a valid number")
|
|
}
|
|
|
|
@Test func belowMinFails() {
|
|
let result = ValidationHelpers.validateNumber("5", fieldName: "Cost", min: 10)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Cost must be at least 10.0")
|
|
}
|
|
|
|
@Test func aboveMaxFails() {
|
|
let result = ValidationHelpers.validateNumber("100", fieldName: "Cost", max: 50)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Cost must be at most 50.0")
|
|
}
|
|
|
|
@Test func withinRangePasses() {
|
|
let result = ValidationHelpers.validateNumber("25", fieldName: "Cost", min: 10, max: 50)
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func negativeNumberValidated() {
|
|
let result = ValidationHelpers.validateNumber("-5", fieldName: "Value", min: 0)
|
|
#expect(!result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Integer Validation Tests
|
|
|
|
struct IntegerValidationTests {
|
|
|
|
@Test func validIntegerPasses() {
|
|
let result = ValidationHelpers.validateInteger("42", fieldName: "Bedrooms")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func decimalFails() {
|
|
let result = ValidationHelpers.validateInteger("3.5", fieldName: "Bedrooms")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Bedrooms must be a whole number")
|
|
}
|
|
|
|
@Test func belowMinFails() {
|
|
let result = ValidationHelpers.validateInteger("0", fieldName: "Bedrooms", min: 1)
|
|
#expect(!result.isValid)
|
|
}
|
|
|
|
@Test func aboveMaxFails() {
|
|
let result = ValidationHelpers.validateInteger("100", fieldName: "Bedrooms", max: 50)
|
|
#expect(!result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Length Validation Tests
|
|
|
|
struct LengthValidationTests {
|
|
|
|
@Test func withinLengthPasses() {
|
|
let result = ValidationHelpers.validateLength("hello", fieldName: "Title", min: 2, max: 100)
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func tooShortFails() {
|
|
let result = ValidationHelpers.validateLength("a", fieldName: "Title", min: 3)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Title must be at least 3 characters")
|
|
}
|
|
|
|
@Test func tooLongFails() {
|
|
let result = ValidationHelpers.validateLength("abcdef", fieldName: "Code", max: 5)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Code must be at most 5 characters")
|
|
}
|
|
|
|
@Test func emptyWithNoMinPasses() {
|
|
let result = ValidationHelpers.validateLength("", fieldName: "Notes")
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - URL Validation Tests
|
|
|
|
struct URLValidationTests {
|
|
|
|
@Test func validURLPasses() {
|
|
let result = ValidationHelpers.validateURL("https://example.com")
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func emptyURLFails() {
|
|
let result = ValidationHelpers.validateURL("")
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "URL is required")
|
|
}
|
|
|
|
@Test func httpURLPasses() {
|
|
let result = ValidationHelpers.validateURL("http://example.com/path")
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - Custom Validation Tests
|
|
|
|
struct CustomValidationTests {
|
|
|
|
@Test func passingCustomValidatorPasses() {
|
|
let result = ValidationHelpers.validateCustom(
|
|
"abc123",
|
|
fieldName: "Code",
|
|
validator: { $0.count == 6 },
|
|
errorMessage: "Code must be exactly 6 characters"
|
|
)
|
|
#expect(result.isValid)
|
|
}
|
|
|
|
@Test func failingCustomValidatorFails() {
|
|
let result = ValidationHelpers.validateCustom(
|
|
"abc",
|
|
fieldName: "Code",
|
|
validator: { $0.count == 6 },
|
|
errorMessage: "Code must be exactly 6 characters"
|
|
)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Code must be exactly 6 characters")
|
|
}
|
|
|
|
@Test func emptyValueInCustomValidatorFails() {
|
|
let result = ValidationHelpers.validateCustom(
|
|
"",
|
|
fieldName: "Code",
|
|
validator: { _ in true },
|
|
errorMessage: "Unused"
|
|
)
|
|
#expect(!result.isValid)
|
|
#expect(result.errorMessage == "Code is required")
|
|
}
|
|
}
|
|
|
|
// MARK: - FormValidator Tests
|
|
|
|
struct FormValidatorTests {
|
|
|
|
@Test func allValidFieldsPass() {
|
|
let validator = FormValidator()
|
|
validator.add(fieldName: "email") { ValidationHelpers.validateEmail("user@example.com") }
|
|
validator.add(fieldName: "name") { ValidationHelpers.validateName("John") }
|
|
|
|
let result = validator.validate()
|
|
#expect(result.isValid)
|
|
#expect(result.errors.isEmpty)
|
|
}
|
|
|
|
@Test func singleInvalidFieldFails() {
|
|
let validator = FormValidator()
|
|
validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
|
|
validator.add(fieldName: "name") { ValidationHelpers.validateName("John") }
|
|
|
|
let result = validator.validate()
|
|
#expect(!result.isValid)
|
|
#expect(result.errors.count == 1)
|
|
#expect(result.errors["email"] != nil)
|
|
}
|
|
|
|
@Test func multipleInvalidFieldsFail() {
|
|
let validator = FormValidator()
|
|
validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
|
|
validator.add(fieldName: "password") { ValidationHelpers.validatePassword("") }
|
|
|
|
let result = validator.validate()
|
|
#expect(!result.isValid)
|
|
#expect(result.errors.count == 2)
|
|
}
|
|
|
|
@Test func clearRemovesValidations() {
|
|
let validator = FormValidator()
|
|
validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
|
|
validator.clear()
|
|
|
|
let result = validator.validate()
|
|
#expect(result.isValid)
|
|
}
|
|
}
|
|
|
|
// MARK: - FormValidationResult Tests
|
|
|
|
struct FormValidationResultTests {
|
|
|
|
@Test func validResultHasNoErrors() {
|
|
let result = FormValidationResult.valid
|
|
#expect(result.isValid)
|
|
#expect(result.errors.isEmpty)
|
|
}
|
|
|
|
@Test func invalidResultHasErrors() {
|
|
let result = FormValidationResult.invalid(["email": "Email is required"])
|
|
#expect(!result.isValid)
|
|
#expect(result.errors["email"] == "Email is required")
|
|
}
|
|
}
|