diff --git a/src/main/java/net/datafaker/idnumbers/SpanishIdNumber.java b/src/main/java/net/datafaker/idnumbers/SpanishIdNumber.java new file mode 100644 index 000000000..7d946763a --- /dev/null +++ b/src/main/java/net/datafaker/idnumbers/SpanishIdNumber.java @@ -0,0 +1,43 @@ +package net.datafaker.idnumbers; + +import net.datafaker.providers.base.BaseProviders; +import net.datafaker.providers.base.IdNumber.IdNumberRequest; +import net.datafaker.providers.base.PersonIdNumber; + +import static net.datafaker.idnumbers.Utils.birthday; +import static net.datafaker.idnumbers.Utils.gender; + +/** + * Spanish national identity number (DNI/NIF). + * See + * Calculation of the NIF/NIE check character. + */ +public class SpanishIdNumber implements IdNumberGenerator { + private static final String CHECK_LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE"; + + @Override + public String countryCode() { + return "ES"; + } + + @Override + public PersonIdNumber generateValid(BaseProviders faker, IdNumberRequest request) { + String digits = faker.number().digits(8); + String idNumber = digits + checksum(digits); + return new PersonIdNumber(idNumber, birthday(faker, request), gender(faker, request)); + } + + @Override + public String generateInvalid(BaseProviders faker) { + String digits = faker.number().digits(8); + char validLetter = checksum(digits); + int validLetterIndex = CHECK_LETTERS.indexOf(validLetter); + char invalidLetter = CHECK_LETTERS.charAt((validLetterIndex + 1) % CHECK_LETTERS.length()); + return digits + invalidLetter; + } + + static char checksum(String digits) { + int number = Integer.parseInt(digits); + return CHECK_LETTERS.charAt(number % CHECK_LETTERS.length()); + } +} diff --git a/src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumberGenerator b/src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumberGenerator index 84ad67452..f61aa443a 100644 --- a/src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumberGenerator +++ b/src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumberGenerator @@ -19,5 +19,6 @@ net.datafaker.idnumbers.RomanianIdNumber net.datafaker.idnumbers.SingaporeIdNumber net.datafaker.idnumbers.SouthAfricanIdNumber net.datafaker.idnumbers.SouthKoreanIdNumber +net.datafaker.idnumbers.SpanishIdNumber net.datafaker.idnumbers.SwedenIdNumber net.datafaker.idnumbers.UkrainianIdNumber diff --git a/src/test/java/net/datafaker/helpers/IdNumberPatterns.java b/src/test/java/net/datafaker/helpers/IdNumberPatterns.java index 3893bd043..957c59c68 100644 --- a/src/test/java/net/datafaker/helpers/IdNumberPatterns.java +++ b/src/test/java/net/datafaker/helpers/IdNumberPatterns.java @@ -15,5 +15,6 @@ public class IdNumberPatterns { public static final Pattern ESTONIAN = Pattern.compile("[1-6][0-9]{10}"); public static final Pattern BRAZILIAN = Pattern.compile("\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}"); public static final Pattern NORWEGIAN = Pattern.compile("\\d{11}"); + public static final Pattern SPANISH = Pattern.compile("\\d{8}[TRWAGMYFPDXBNJZSQVHLCKE]"); } diff --git a/src/test/java/net/datafaker/idnumbers/SpanishIdNumberTest.java b/src/test/java/net/datafaker/idnumbers/SpanishIdNumberTest.java new file mode 100644 index 000000000..c9d39a260 --- /dev/null +++ b/src/test/java/net/datafaker/idnumbers/SpanishIdNumberTest.java @@ -0,0 +1,45 @@ +package net.datafaker.idnumbers; + +import net.datafaker.Faker; +import net.datafaker.helpers.IdNumberPatterns; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class SpanishIdNumberTest { + private final SpanishIdNumber generator = new SpanishIdNumber(); + private final Faker faker = new Faker(); + + @ParameterizedTest + @CsvSource({ + "00000000, T", + "00000001, R", + "12345678, Z", + "99999999, R", + }) + void checksum(String digits, char expectedLetter) { + assertThat(SpanishIdNumber.checksum(digits)).isEqualTo(expectedLetter); + } + + @RepeatedTest(100) + void valid() { + String generated = generator.generateValid(faker); + + assertThat(generated).matches(IdNumberPatterns.SPANISH); + assertThat(generated.charAt(8)) + .as("DNI check letter") + .isEqualTo(SpanishIdNumber.checksum(generated.substring(0, 8))); + } + + @RepeatedTest(100) + void invalid() { + String generated = generator.generateInvalid(faker); + + assertThat(generated).matches(IdNumberPatterns.SPANISH); + assertThat(generated.charAt(8)) + .as("DNI check letter should be invalid") + .isNotEqualTo(SpanishIdNumber.checksum(generated.substring(0, 8))); + } +} diff --git a/src/test/java/net/datafaker/providers/base/IdNumberTest.java b/src/test/java/net/datafaker/providers/base/IdNumberTest.java index fe6f3e900..26b628292 100644 --- a/src/test/java/net/datafaker/providers/base/IdNumberTest.java +++ b/src/test/java/net/datafaker/providers/base/IdNumberTest.java @@ -42,6 +42,7 @@ class IdNumberTest { private static final Faker HUNGARIAN = new Faker(new Locale("hu", "HU")); private static final Faker BRAZILIAN = new Faker(new Locale("pt", "BR")); private static final Faker NORWEGIAN = new Faker(new Locale("no", "NO")); + private static final Faker SPANISH = new Faker(new Locale("es", "ES")); @Test void testValid() { @@ -278,6 +279,16 @@ void norwegianIdNumber_invalid() { assertThatPin(NORWEGIAN.idNumber().invalid()).matches(IdNumberPatterns.NORWEGIAN); } + @RepeatedTest(100) + void spanishIdNumber_valid() { + assertThatPin(SPANISH.idNumber().valid()).matches(IdNumberPatterns.SPANISH); + } + + @RepeatedTest(100) + void spanishIdNumber_invalid() { + assertThatPin(SPANISH.idNumber().invalid()).matches(IdNumberPatterns.SPANISH); + } + private static AbstractStringAssert assertThatPin(String pin) { return assertThat(pin) .as(() -> "PIN: %s".formatted(pin));