TM-SGNL-iOS/Signal/test/util/PaymentDetailsValidityTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

274 lines
9.3 KiB
Swift

//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import XCTest
@testable import Signal
final class PaymentDetailsValidityTest: XCTestCase {
typealias CardType = CreditAndDebitCards.CardType
// MARK: Cards
func testCardType() {
func cardType(_ number: String) -> CardType {
CreditAndDebitCards.cardType(ofNumber: number)
}
let amex: [String] = ["34", "37", "343452000000306", "371449635398431", "378282246310005"]
for number in amex {
XCTAssertEqual(cardType(number), .americanExpress)
}
let unionpay: [String] = ["62", "81", "6200000000000004"]
for number in unionpay {
XCTAssertEqual(cardType(number), .unionPay)
}
let other: [String] = [
"",
"X",
"4",
"4111111111111111",
"4242424242424242",
"5555555555554444",
"2223003122003222",
"6011111111111117",
"3056930009020004",
"3566002020360505"
]
for number in other {
XCTAssertEqual(cardType(number), .other)
}
}
func testCardTypeCvvCount() {
XCTAssertEqual(CardType.americanExpress.cvvCount, 4)
XCTAssertEqual(CardType.unionPay.cvvCount, 3)
XCTAssertEqual(CardType.other.cvvCount, 3)
}
func testValidityOfNumber() {
func n(_ number: String, focused: Bool = false) -> CreditAndDebitCards.Validity {
CreditAndDebitCards.validity(ofNumber: number, isNumberFieldFocused: focused)
}
// Typing
XCTAssertEqual(n(""), .potentiallyValid)
XCTAssertEqual(n("4"), .potentiallyValid)
XCTAssertEqual(n("42"), .potentiallyValid)
XCTAssertEqual(n("42424242424"), .potentiallyValid)
// Fully valid
XCTAssertEqual(n("424242424242"), .fullyValid)
XCTAssertEqual(n("424242424242424242"), .fullyValid)
// Luhn-invalid cards
XCTAssertEqual(n("4242424242424"), .invalid(()))
XCTAssertEqual(n("4242424242424", focused: true), .potentiallyValid)
// UnionPay cards
XCTAssertEqual(n("6200000000000004"), .fullyValid)
XCTAssertEqual(n("6200000000000005"), .fullyValid)
// Too long
XCTAssertEqual(n("42424242424242424242"), .invalid(()))
// Invalid characters
XCTAssertEqual(n("X"), .invalid(()))
XCTAssertEqual(n("42X"), .invalid(()))
XCTAssertEqual(n("424242424242X"), .invalid(()))
XCTAssertEqual(n("4242 4242 4242 4242"), .invalid(()))
}
func testValidityOfExpirationDate() {
func d(_ month: String, _ year: String, currentYear: Int = 2020) -> CreditAndDebitCards.Validity {
CreditAndDebitCards.validity(
ofExpirationMonth: month,
andYear: year,
currentMonth: 3,
currentYear: currentYear
)
}
XCTAssertEqual(d("", ""), .potentiallyValid)
XCTAssertEqual(d("0", ""), .potentiallyValid)
XCTAssertEqual(d("1", ""), .potentiallyValid)
XCTAssertEqual(d("9", ""), .potentiallyValid)
XCTAssertEqual(d("01", ""), .potentiallyValid)
XCTAssertEqual(d("09", ""), .potentiallyValid)
XCTAssertEqual(d("12", ""), .potentiallyValid)
XCTAssertEqual(d("", "0"), .potentiallyValid)
XCTAssertEqual(d("", "1"), .potentiallyValid)
XCTAssertEqual(d("", "9"), .potentiallyValid)
XCTAssertEqual(d("", "00"), .potentiallyValid)
XCTAssertEqual(d("", "01"), .potentiallyValid)
XCTAssertEqual(d("", "99"), .potentiallyValid)
XCTAssertEqual(d("3", "20"), .fullyValid)
XCTAssertEqual(d("03", "20"), .fullyValid)
XCTAssertEqual(d("4", "20"), .fullyValid)
XCTAssertEqual(d("12", "20"), .fullyValid)
XCTAssertEqual(d("3", "21"), .fullyValid)
XCTAssertEqual(d("3", "40"), .fullyValid)
XCTAssertEqual(d("2", "20"), .invalid(()))
XCTAssertEqual(d("3", "41"), .invalid(()))
XCTAssertEqual(d("3", "41", currentYear: 2021), .fullyValid)
XCTAssertEqual(d("01", "99", currentYear: 2098), .fullyValid)
XCTAssertEqual(d("3", "00", currentYear: 2099), .fullyValid)
XCTAssertEqual(d("4", "00", currentYear: 2099), .fullyValid)
XCTAssertEqual(d("3", "19", currentYear: 2099), .fullyValid)
XCTAssertEqual(d("12", "19", currentYear: 2099), .fullyValid)
XCTAssertEqual(d("00", "20"), .invalid(()))
XCTAssertEqual(d("X", ""), .invalid(()))
XCTAssertEqual(d("1X", ""), .invalid(()))
XCTAssertEqual(d("123", ""), .invalid(()))
XCTAssertEqual(d("", "X"), .invalid(()))
XCTAssertEqual(d("", "2X"), .invalid(()))
XCTAssertEqual(d("", "202"), .invalid(()))
XCTAssertEqual(d("", "2020"), .invalid(()))
XCTAssertEqual(d("X", "X"), .invalid(()))
XCTAssertEqual(d(" 3", "40"), .invalid(()))
XCTAssertEqual(d("3", " 40"), .invalid(()))
}
func testValidityOfCvv() {
func c(_ cvv: String, _ cardType: CardType) -> CreditAndDebitCards.Validity {
CreditAndDebitCards.validity(ofCvv: cvv, cardType: cardType)
}
let threeDigitTypes: [CardType] = [.unionPay, .other]
for cardType in threeDigitTypes {
XCTAssertEqual(c("", cardType), .potentiallyValid)
XCTAssertEqual(c("1", cardType), .potentiallyValid)
XCTAssertEqual(c("12", cardType), .potentiallyValid)
XCTAssertEqual(c("123", cardType), .fullyValid)
XCTAssertEqual(c("1234", cardType), .invalid(()))
}
XCTAssertEqual(c("", .americanExpress), .potentiallyValid)
XCTAssertEqual(c("1", .americanExpress), .potentiallyValid)
XCTAssertEqual(c("12", .americanExpress), .potentiallyValid)
XCTAssertEqual(c("123", .americanExpress), .potentiallyValid)
XCTAssertEqual(c("1234", .americanExpress), .fullyValid)
XCTAssertEqual(c("12345", .americanExpress), .invalid(()))
XCTAssertEqual(c("X", .other), .invalid(()))
XCTAssertEqual(c("1X", .other), .invalid(()))
XCTAssertEqual(c("X1", .other), .invalid(()))
XCTAssertEqual(c(" 123", .other), .invalid(()))
}
// MARK: SEPA
func testValidityOfIBAN() throws {
func n(_ number: String, focused: Bool) -> SEPABankAccounts.IBANValidity {
SEPABankAccounts.validity(of: number, isFieldFocused: focused)
}
let supportedCountryCodes = [
"AT",
"BE",
"BG",
"HR",
"CY",
"CZ",
"DK",
"EE",
"FI",
"FR",
"DE",
"GR",
"HU",
"IE",
"IT",
"LV",
"LT",
"LU",
"NL",
"MT",
"PL",
"PT",
"RO",
"SK",
"SI",
"ES",
"SE",
"CH",
"GB",
"SM",
"VA",
"AD",
"MC",
"IS",
"NO",
"LI",
]
for countryCode in supportedCountryCodes {
XCTAssertEqual(n(countryCode, focused: true), .potentiallyValid)
}
let stripeTestIBANs = [
"AT611904300234573201",
"BE62510007547061",
"HR7624020064583467589",
"EE382200221020145685",
"FI2112345600000785",
"FR1420041010050500013M02606",
"DE89370400440532013000",
"GI60MPFS599327643783385",
"IE29AIBK93115212345678",
"LI0508800636123378777",
"LT121000011101001000",
"LU280019400644750000",
"NO9386011117947",
"PT50000201231234567890154",
"ES0700120345030000067890",
"SE3550000000054910000003",
"CH9300762011623852957",
"GB82WEST12345698765432",
]
for iban in stripeTestIBANs {
XCTAssertEqual(n(iban, focused: false), .fullyValid)
XCTAssertEqual(n(iban, focused: true), .fullyValid)
XCTAssertEqual(n(String(iban.dropLast()), focused: true), .potentiallyValid)
XCTAssertEqual(n(String(iban.dropLast()), focused: false), .invalid(.tooShort))
XCTAssertEqual(n(iban + "0", focused: true), .invalid(.tooLong))
}
let ibanPlus1 = "AT611904300234573202"
XCTAssertEqual(n(ibanPlus1, focused: true), .invalid(.invalidCheck))
let ibanPlus97 = "AT611904300234573298"
XCTAssertEqual(n(ibanPlus97, focused: true), .fullyValid)
XCTAssertEqual(n("XX", focused: true), .invalid(.invalidCountry))
XCTAssertEqual(n("EE.0", focused: true), .invalid(.invalidCharacters))
}
}
private func XCTAssertEqual(
_ expression1: @autoclosure () throws -> CreditAndDebitCards.Validity,
_ expression2: @autoclosure () throws -> CreditAndDebitCards.Validity,
file: StaticString = #filePath,
line: UInt = #line
) rethrows {
let lhs = try expression1()
let rhs = try expression2()
switch (lhs, rhs) {
case (.fullyValid, .fullyValid):
break
case (.potentiallyValid, .potentiallyValid):
break
case (.invalid, .invalid):
break
default:
XCTFail("(\"\(lhs)\") is not equal to (\"\(rhs)\")", file: file, line: line)
}
}