diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index ac302c6d1e8..a68b66ae9de 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -1,4 +1,5 @@ import type { Faker } from '../..'; +import { luhnCheckValue } from './luhn-check'; /** * Module with various helper methods that transform the method input rather than returning values from locales. @@ -141,30 +142,10 @@ export class Helpers { ): string { // default values required for calling method without arguments - // Function calculating the Luhn checksum of a number string - const getCheckBit = (number: number[]) => { - number.reverse(); - number = number.map((num, index) => { - if (index % 2 === 0) { - num *= 2; - if (num > 9) { - num -= 9; - } - } - return num; - }); - const sum = number.reduce((prev, curr) => prev + curr); - return sum % 10; - }; - string = this.regexpStyleStringParse(string); // replace [4-9] with a random number in range etc... string = this.replaceSymbolWithNumber(string, symbol); // replace ### with random numbers - const numberList = string - .replace(/\D/g, '') - .split('') - .map((num) => parseInt(num)); - const checkNum = getCheckBit(numberList); + const checkNum = luhnCheckValue(string); return string.replace('L', String(checkNum)); } diff --git a/src/modules/helpers/luhn-check.ts b/src/modules/helpers/luhn-check.ts new file mode 100644 index 00000000000..9cfcc577798 --- /dev/null +++ b/src/modules/helpers/luhn-check.ts @@ -0,0 +1,42 @@ +/** + * Checks that the given string passes the luhn algorithm. + * + * @param str The string to validate. + */ +export function luhnCheck(str: string): boolean { + return luhnChecksum(str) === 0; +} + +/** + * Calculates the luhn check value for the given string. + * + * @param str The string to calculate the check value for. + * May contain the `L` placeholder at the end. + */ +export function luhnCheckValue(str: string): number { + const checksum = luhnChecksum(str.replace(/L?$/, '0')); + return checksum === 0 ? 0 : 10 - checksum; +} + +/** + * Calculates the luhn checksum value for the given value. + * + * @param str The string to generate the checksum for. + */ +function luhnChecksum(str: string): number { + str = str.replace(/[\s-]/g, ''); + let sum = 0; + let alternate = false; + for (let i = str.length - 1; i >= 0; i--) { + let n = parseInt(str.substring(i, i + 1)); + if (alternate) { + n *= 2; + if (n > 9) { + n = (n % 10) + 1; + } + } + sum += n; + alternate = !alternate; + } + return sum % 10; +} diff --git a/test/finance.spec.ts b/test/finance.spec.ts index 0e19aa53542..ef3fb045d72 100644 --- a/test/finance.spec.ts +++ b/test/finance.spec.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest'; import { faker } from '../src'; import { FakerError } from '../src/errors/faker-error'; import ibanLib from '../src/modules/finance/iban'; -import { luhnCheck } from './support/luhnCheck'; +import { luhnCheck } from '../src/modules/helpers/luhn-check'; const seedRuns = [ { @@ -19,7 +19,7 @@ const seedRuns = [ currencySymbol: '₱', bitcoinAddress: '3XbJMAAara64sSkA9HD24YHQWd1b', litecoinAddress: '3XbJMAAara64sSkA9HD24YHQWd1b', - creditCardNumber: '3581-7755-1410-0486', + creditCardNumber: '3581-7755-1410-0484', creditCardCVV: '379', pin: '3791', ethereumAddress: '0x8be4abdd39321ad7d3fe01ffce404f4d6db0906b', @@ -43,7 +43,7 @@ const seedRuns = [ currencySymbol: '$', bitcoinAddress: '3adhxs2jewAgkYgJi7No6Cn8JZa', litecoinAddress: 'Madhxs2jewAgkYgJi7No6Cn8JZar', - creditCardNumber: '6011-6212-2540-3255-2392', + creditCardNumber: '6011-6212-2540-3255-2398', creditCardCVV: '251', pin: '2512', ethereumAddress: '0x5c346ba075bd57f5a62b82d72af39cbbb07a98cb', @@ -67,7 +67,7 @@ const seedRuns = [ currencySymbol: '₭', bitcoinAddress: '1TMe8Z3EaFdLqmaGKP1LEEJQVriSZRZdsA', litecoinAddress: 'MTMe8Z3EaFdLqmaGKP1LEEJQVriSZRZds', - creditCardNumber: '4872190616276', + creditCardNumber: '4872190616274', creditCardCVV: '948', pin: '9487', ethereumAddress: '0xeadb42f0e3f4a973fab0aeefce96dfcf49cd438d', diff --git a/test/helpers.spec.ts b/test/helpers.spec.ts index 155d2d97efd..f5f4e96f8ff 100644 --- a/test/helpers.spec.ts +++ b/test/helpers.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest'; import { faker } from '../src'; -import { luhnCheck } from './support/luhnCheck'; +import { luhnCheck } from '../src/modules/helpers/luhn-check'; const seededRuns = [ { @@ -9,7 +9,7 @@ const seededRuns = [ slugify: '', replaceSymbolWithNumber: '', replaceSymbols: '', - replaceCreditCardSymbols: '6453-3791-7755-1410-0481', + replaceCreditCardSymbols: '6453-3791-7755-1410-0489', repeatString: '', regexpStyleStringParse: '', shuffle: [], @@ -23,7 +23,7 @@ const seededRuns = [ slugify: '', replaceSymbolWithNumber: '', replaceSymbols: '', - replaceCreditCardSymbols: '6453-2512-2540-3255-2391', + replaceCreditCardSymbols: '6453-2512-2540-3255-2399', repeatString: '', regexpStyleStringParse: '', shuffle: [], @@ -37,7 +37,7 @@ const seededRuns = [ slugify: '', replaceSymbolWithNumber: '', replaceSymbols: '', - replaceCreditCardSymbols: '6453-9487-2190-6162-7436', + replaceCreditCardSymbols: '6453-9487-2190-6162-7434', repeatString: '', regexpStyleStringParse: '', shuffle: [], diff --git a/test/phone.spec.ts b/test/phone.spec.ts index 17180156008..e0ff1996fa2 100644 --- a/test/phone.spec.ts +++ b/test/phone.spec.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { faker } from '../src'; -import { luhnCheck } from './support/luhnCheck'; +import { luhnCheck } from '../src/modules/helpers/luhn-check'; const seededRuns = [ { @@ -35,7 +35,7 @@ const seededRuns = [ noArgs: '(!##) !##-####', }, imei: { - noArgs: '25-122540-325523-6', + noArgs: '25-122540-325523-4', }, }, }, @@ -53,7 +53,7 @@ const seededRuns = [ noArgs: '1-!##-!##-#### x#####', }, imei: { - noArgs: '94-872190-616274-6', + noArgs: '94-872190-616274-4', }, }, }, diff --git a/test/support/luhnCheck.ts b/test/support/luhnCheck.ts deleted file mode 100644 index fb4e0bb0fa9..00000000000 --- a/test/support/luhnCheck.ts +++ /dev/null @@ -1,18 +0,0 @@ -export function luhnCheck(number: string): boolean { - number = number.replace(/\D/g, ''); - let split: string[] | number[] = number.split(''); - split = split.map((num) => parseInt(num)); - const check = split.pop(); - split.reverse(); - split = split.map((num, index) => { - if (index % 2 === 0) { - num *= 2; - if (num > 9) { - num -= 9; - } - } - return num; - }); - const sum = split.reduce((prev, curr) => prev + curr); - return sum % 10 === check; -}