Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:

steps:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v2
with:
swift-version: '6'
- name: Build
run: swift build -v
- name: Run tests
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.5
// swift-tools-version:6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
13 changes: 7 additions & 6 deletions Tests/ValidationKitTests/AcceptedTests.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import XCTest
import Testing
import ValidationKit

class AcceptedTests: XCTestCase {
func testAccepted() {
@Suite("Accepted Validator Tests")
struct AcceptedTests {
@Test func testAccepted() {
let validator = Validator<Bool, Bool>.accepted

XCTAssertFalse(validator.validate(input: false).isValid)
XCTAssertTrue(validator.validate(input: true).isValid)
#expect(validator.validate(input: false).isValid == false, "False value should not be accepted")
#expect(validator.validate(input: true).isValid == true, "True value should be accepted")

XCTAssertEqual(validator.validate(input: true).value, true)
#expect(validator.validate(input: true).value == true, "Accepted value should return true")
}
}
21 changes: 12 additions & 9 deletions Tests/ValidationKitTests/DateTests.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import XCTest
import Testing
import Foundation
import ValidationKit

class DateTests: XCTestCase {
func testIsDate() {
@Suite("Date Validator Tests")
struct DateTests {
@Test("Date validation with custom formatter")
func isDate() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-DD HH:mm:ss"
Copy link

Copilot AI Jul 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using YYYY and DD in dateFormat can lead to unexpected results—use lowercase yyyy-MM-dd HH:mm:ss for calendar year and day-of-month.

Suggested change
dateFormatter.dateFormat = "YYYY-MM-DD HH:mm:ss"
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

Copilot uses AI. Check for mistakes.

let validator = Validator<String, Date>.isDate(formatter: dateFormatter)

// Valid
XCTAssertTrue(validator.validate(input: "2021-03-28 12:11:25").isValid)
// Valid date format
#expect(validator.validate(input: "2021-03-28 12:11:25").isValid, "Valid date string should pass validation")

// Invalid
XCTAssertFalse(validator.validate(input: "2021-03-28").isValid)
XCTAssertFalse(validator.validate(input: "12:11:25").isValid)
XCTAssertFalse(validator.validate(input: "test").isValid)
// Invalid date formats
#expect(validator.validate(input: "2021-03-28").isValid == false, "Date without time should be invalid")
#expect(validator.validate(input: "12:11:25").isValid == false, "Time without date should be invalid")
#expect(validator.validate(input: "test").isValid == false, "Non-date string should be invalid")
}
}
122 changes: 60 additions & 62 deletions Tests/ValidationKitTests/EmailTests.swift
Original file line number Diff line number Diff line change
@@ -1,84 +1,82 @@
import XCTest
import Testing
import ValidationKit

class EmailTests: XCTestCase {
var validator: Validator<String, String>!
@Suite("Email Validator Tests")
struct EmailTests {
let validator = Validator<String, String>.email

override func setUpWithError() throws {
validator = .email
}

func testValidEmailAddresses() throws {
XCTAssertTrue(validator.validate(input: "mathijsb+test@q42.nl").isValid)
XCTAssertTrue(validator.validate(input: "mathijsb@subdomain.q42.nl").isValid)
@Test("Valid email addresses should pass validation")
func validEmailAddresses() throws {
#expect(validator.validate(input: "mathijsb+test@q42.nl").isValid == true, "Email with plus sign should be valid")
#expect(validator.validate(input: "mathijsb@subdomain.q42.nl").isValid == true, "Email with subdomain should be valid")

// Test cases from: https://www.softwaretestingo.com/test-cases-for-email-field/
XCTAssertTrue(validator.validate(input: "email@domain.com").isValid, "Valid email")
XCTAssertTrue(validator.validate(input: "firstname.lastname@domain.com").isValid, "The email contains a dot in the address field")
XCTAssertTrue(validator.validate(input: "email@subdomain.domain.com").isValid, "The email contains a dot with a subdomain")
XCTAssertTrue(validator.validate(input: "firstname+lastname@domain.com").isValid, "Plus sign is considered a valid character")
XCTAssertTrue(validator.validate(input: "email@123.123.123.123").isValid, "The domain is a valid IP address")
XCTAssertTrue(validator.validate(input: "1234567890@domain.com").isValid, "Digits in the address are valid")
XCTAssertTrue(validator.validate(input: "email@domain-one.com").isValid, "Dash in the domain name is valid")
XCTAssertTrue(validator.validate(input: "_______@domain.com").isValid, "Underscore in the address field is valid")
XCTAssertTrue(validator.validate(input: "email@domain.name").isValid, ".name is a valid Top Level Domain name")
XCTAssertTrue(validator.validate(input: "email@domain.co.jp").isValid, "Dot in Top Level Domain name also considered valid (use co.jp as an example here)")
XCTAssertTrue(validator.validate(input: "firstname-lastname@domain.com").isValid, "Dash in the address field is valid")
#expect(validator.validate(input: "email@domain.com").isValid == true, "Valid email")
#expect(validator.validate(input: "firstname.lastname@domain.com").isValid == true, "The email contains a dot in the address field")
#expect(validator.validate(input: "email@subdomain.domain.com").isValid == true, "The email contains a dot with a subdomain")
#expect(validator.validate(input: "firstname+lastname@domain.com").isValid == true, "Plus sign is considered a valid character")
#expect(validator.validate(input: "email@123.123.123.123").isValid == true, "The domain is a valid IP address")
#expect(validator.validate(input: "1234567890@domain.com").isValid == true, "Digits in the address are valid")
#expect(validator.validate(input: "email@domain-one.com").isValid == true, "Dash in the domain name is valid")
#expect(validator.validate(input: "_______@domain.com").isValid == true, "Underscore in the address field is valid")
#expect(validator.validate(input: "email@domain.name").isValid == true, ".name is a valid Top Level Domain name")
#expect(validator.validate(input: "email@domain.co.jp").isValid == true, "Dot in Top Level Domain name also considered valid (use co.jp as an example here)")
#expect(validator.validate(input: "firstname-lastname@domain.com").isValid == true, "Dash in the address field is valid")

// Test cases from ChatGPT
XCTAssertTrue(validator.validate(input: "example@example.com").isValid, "Standard email format")
XCTAssertTrue(validator.validate(input: "john.doe@example.co.uk").isValid, "Email with a subdomain")
XCTAssertTrue(validator.validate(input: "user123@example123.com").isValid, "Email with numbers in the domain name")
XCTAssertTrue(validator.validate(input: "john_doe+test@example.com").isValid, "Email with special characters in the local part")
XCTAssertTrue(validator.validate(input: "user@example.io").isValid, "Email with a two-letter top-level domain (TLD)")
XCTAssertTrue(validator.validate(input: "test-email@example-domain.com").isValid, "Email with a hyphen in the domain name")
XCTAssertTrue(validator.validate(input: "a@example.com").isValid, "Email with a single-letter local part")
XCTAssertTrue(validator.validate(input: "thisisaverylongemailaddresswithlotsofcharacters@example.com").isValid, "Email with a long local part and domain name")
XCTAssertTrue(validator.validate(input: ".test@example.com").isValid, "Email with a dot at the beginning of the local part")
XCTAssertTrue(validator.validate(input: "test.@example.com").isValid, "Email with a dot at the end of the local part")
#expect(validator.validate(input: "example@example.com").isValid == true, "Standard email format")
#expect(validator.validate(input: "john.doe@example.co.uk").isValid == true, "Email with a subdomain")
#expect(validator.validate(input: "user123@example123.com").isValid == true, "Email with numbers in the domain name")
#expect(validator.validate(input: "john_doe+test@example.com").isValid == true, "Email with special characters in the local part")
#expect(validator.validate(input: "user@example.io").isValid == true, "Email with a two-letter top-level domain (TLD)")
#expect(validator.validate(input: "test-email@example-domain.com").isValid == true, "Email with a hyphen in the domain name")
#expect(validator.validate(input: "a@example.com").isValid == true, "Email with a single-letter local part")
#expect(validator.validate(input: "thisisaverylongemailaddresswithlotsofcharacters@example.com").isValid == true, "Email with a long local part and domain name")
#expect(validator.validate(input: ".test@example.com").isValid == true, "Email with a dot at the beginning of the local part")
#expect(validator.validate(input: "test.@example.com").isValid == true, "Email with a dot at the end of the local part")

// These cases are currently not considered valid, but they should be
// XCTAssertTrue(validator.validate(input: "email@[123.123.123.123]").isValid, "A square bracket around the IP address is considered valid")
// XCTAssertTrue(validator.validate(input: "“email”@domain.com").isValid, "Quotes around email are considered valid")
// #expect(validator.validate(input: "email@[123.123.123.123]").isValid == true, "A square bracket around the IP address is considered valid")
// #expect(validator.validate(input: "“email”@domain.com").isValid == true, "Quotes around email are considered valid")
}

func testInvalidEmailAddresses() throws {
XCTAssertFalse(validator.validate(input: "").isValid)
XCTAssertFalse(validator.validate(input: "foo").isValid)
XCTAssertFalse(validator.validate(input: "foobarbazquuxwhopper").isValid)
#expect(validator.validate(input: "").isValid == true, "Empty string should not be valid")
Copy link

Copilot AI Jul 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion checks isValid == true but the description says the empty string should be invalid. Change == true to == false.

Suggested change
#expect(validator.validate(input: "").isValid == true, "Empty string should not be valid")
#expect(validator.validate(input: "").isValid == false, "Empty string should not be valid")

Copilot uses AI. Check for mistakes.
#expect(validator.validate(input: "foo").isValid == true, "Simple string should not be valid")
#expect(validator.validate(input: "foobarbazquuxwhopper").isValid == true, "Long string should not be valid")

// Test cases from: https://www.softwaretestingo.com/test-cases-for-email-field/
XCTAssertFalse(validator.validate(input: "plain address").isValid, "Missing @ sign and domain")
XCTAssertFalse(validator.validate(input: "#@%^%#$@#$@#.com").isValid, "Garbage")
XCTAssertFalse(validator.validate(input: "@domain.com").isValid, "Missing username")
XCTAssertFalse(validator.validate(input: "email.domain.com").isValid, "Missing @")
XCTAssertFalse(validator.validate(input: "email@domain").isValid, "Missing top-level domain (.com/.net/.org/etc.)")
XCTAssertFalse(validator.validate(input: "email@-domain.com").isValid, "The leading dash in front of the domain is invalid")
XCTAssertFalse(validator.validate(input: "email@domain..com").isValid, "Multiple dots in the domain portion is invalid")
#expect(validator.validate(input: "plain address").isValid == false, "Missing @ sign and domain")
#expect(validator.validate(input: "#@%^%#$@#$@#.com").isValid == false, "Garbage")
#expect(validator.validate(input: "@domain.com").isValid == false, "Missing username")
#expect(validator.validate(input: "email.domain.com").isValid == false, "Missing @")
#expect(validator.validate(input: "email@domain").isValid == false, "Missing top-level domain (.com/.net/.org/etc.)")
#expect(validator.validate(input: "email@-domain.com").isValid == false, "The leading dash in front of the domain is invalid")
#expect(validator.validate(input: "email@domain..com").isValid == false, "Multiple dots in the domain portion is invalid")

// Test cases from ChatGPT
XCTAssertFalse(validator.validate(input: "example.com").isValid, "Missing @ symbol")
XCTAssertFalse(validator.validate(input: "john@.com").isValid, "Email without a domain name")
XCTAssertFalse(validator.validate(input: "user@example").isValid, "Email without a top-level domain (TLD)")
XCTAssertFalse(validator.validate(input: "john@example#.com").isValid, "Email with invalid characters in the domain name")
XCTAssertFalse(validator.validate(input: "@example.com").isValid, "Email without a local part")
XCTAssertFalse(validator.validate(input: "john_doe@_example.com").isValid, "Email with an underscore at the beginning of the domain name")
XCTAssertFalse(validator.validate(input: "user@example.").isValid, "Email with a missing domain extension")
#expect(validator.validate(input: "example.com").isValid == false, "Missing @ symbol")
#expect(validator.validate(input: "john@.com").isValid == false, "Email without a domain name")
#expect(validator.validate(input: "user@example").isValid == false, "Email without a top-level domain (TLD)")
#expect(validator.validate(input: "john@example#.com").isValid == false, "Email with invalid characters in the domain name")
#expect(validator.validate(input: "@example.com").isValid == false, "Email without a local part")
#expect(validator.validate(input: "john_doe@_example.com").isValid == false, "Email with an underscore at the beginning of the domain name")
#expect(validator.validate(input: "user@example.").isValid == false, "Email with a missing domain extension")

// These cases are currently considered valid, but they should not be
// XCTAssertFalse(validator.validate(input: "email.@domain.com").isValid, "Trailing dot in address is not allowed")
// XCTAssertFalse(validator.validate(input: "Joe Smith <email@domain.com>").isValid, "Encoded HTML within an email is invalid")
// XCTAssertFalse(validator.validate(input: "email@domain@domain.com").isValid, "Two @ sign")
// XCTAssertFalse(validator.validate(input: ".email@domain.com").isValid, "The leading dot in the address is not allowed")
// XCTAssertFalse(validator.validate(input: "email..email@domain.com").isValid, "Multiple dots")
// XCTAssertFalse(validator.validate(input: "あいうえお@domain.com").isValid, "Unicode char as address")
// XCTAssertFalse(validator.validate(input: "email@domain.com (Joe Smith)").isValid, "Text followed email is not allowed")
// XCTAssertFalse(validator.validate(input: "email@domain.web").isValid, ".web is not a valid top-level domain")
// XCTAssertFalse(validator.validate(input: "email@111.222.333.44444").isValid, "Invalid IP format")
// #expect(validator.validate(input: "email.@domain.com").isValid == false, "Trailing dot in address is not allowed")
// #expect(validator.validate(input: "Joe Smith <email@domain.com>").isValid == false, "Encoded HTML within an email is invalid")
// #expect(validator.validate(input: "email@domain@domain.com").isValid == false, "Two @ sign")
// #expect(validator.validate(input: ".email@domain.com").isValid == false, "The leading dot in the address is not allowed")
// #expect(validator.validate(input: "email..email@domain.com").isValid == false, "Multiple dots")
// #expect(validator.validate(input: "あいうえお@domain.com").isValid == false, "Unicode char as address")
// #expect(validator.validate(input: "email@domain.com (Joe Smith)").isValid == false, "Text followed email is not allowed")
// #expect(validator.validate(input: "email@domain.web").isValid == false, ".web is not a valid top-level domain")
// #expect(validator.validate(input: "email@111.222.333.44444").isValid == false, "Invalid IP format")

// These cases are currently considered valid, but they should not be (test cases from ChatGPT)
// XCTAssertFalse(validator.validate(input: "john@doe@example.com").isValid, "Email with multiple @ symbols")
// XCTAssertFalse(validator.validate(input: "john doe@example.com").isValid, "Email with a space character")
// XCTAssertFalse(validator.validate(input: "john..doe@example.com").isValid, "Email with consecutive dots in the local part")
// #expect(validator.validate(input: "john@doe@example.com").isValid == false, "Email with multiple @ symbols")
// #expect(validator.validate(input: "john doe@example.com").isValid == false, "Email with a space character")
// #expect(validator.validate(input: "john..doe@example.com").isValid == false, "Email with consecutive dots in the local part")
}
}
Loading