From 679ac893ffddbb44fbd149752a25e77d6fb61866 Mon Sep 17 00:00:00 2001 From: Z004R8TW Date: Fri, 2 Jan 2026 21:01:11 +0530 Subject: [PATCH] feat: Add es_PE locale with providers --- faker/providers/address/es_PE/__init__.py | 84 ++++++++++++ faker/providers/automotive/es_PE/__init__.py | 49 +++++++ faker/providers/bank/es_PE/__init__.py | 39 ++++++ faker/providers/color/es_PE/__init__.py | 7 + faker/providers/company/es_PE/__init__.py | 124 ++++++++++++++++++ faker/providers/currency/es_PE/__init__.py | 8 ++ faker/providers/date_time/es_PE/__init__.py | 5 + faker/providers/internet/es_PE/__init__.py | 27 ++++ faker/providers/job/es_PE/__init__.py | 5 + faker/providers/person/es_PE/__init__.py | 102 ++++++++++++++ .../providers/phone_number/es_PE/__init__.py | 90 +++++++++++++ faker/providers/ssn/es_PE/__init__.py | 60 +++++++++ tests/providers/test_address.py | 57 ++++++++ tests/providers/test_automotive.py | 12 ++ tests/providers/test_bank.py | 15 +++ tests/providers/test_color.py | 17 +++ tests/providers/test_company.py | 16 +++ tests/providers/test_currency.py | 29 ++++ tests/providers/test_date_time.py | 14 ++ tests/providers/test_internet.py | 13 ++ tests/providers/test_job.py | 8 ++ tests/providers/test_person.py | 27 ++++ tests/providers/test_phone_number.py | 16 +++ tests/providers/test_ssn.py | 35 +++++ 24 files changed, 859 insertions(+) create mode 100644 faker/providers/address/es_PE/__init__.py create mode 100644 faker/providers/automotive/es_PE/__init__.py create mode 100644 faker/providers/bank/es_PE/__init__.py create mode 100644 faker/providers/color/es_PE/__init__.py create mode 100644 faker/providers/company/es_PE/__init__.py create mode 100644 faker/providers/currency/es_PE/__init__.py create mode 100644 faker/providers/date_time/es_PE/__init__.py create mode 100644 faker/providers/internet/es_PE/__init__.py create mode 100644 faker/providers/job/es_PE/__init__.py create mode 100644 faker/providers/person/es_PE/__init__.py create mode 100644 faker/providers/phone_number/es_PE/__init__.py create mode 100644 faker/providers/ssn/es_PE/__init__.py diff --git a/faker/providers/address/es_PE/__init__.py b/faker/providers/address/es_PE/__init__.py new file mode 100644 index 00000000000..be95e4db521 --- /dev/null +++ b/faker/providers/address/es_PE/__init__.py @@ -0,0 +1,84 @@ +from ..es import Provider as AddressProvider + +class Provider(AddressProvider): + """ + A Faker provider for the Spanish-Peru (es_PE) locale. + This inherits from the base 'es' provider and adds Peru-specific data. + + Sources: + - Departments: https://en.wikipedia.org/wiki/Departments_of_Peru + - Cities: https://en.wikipedia.org/wiki/List_of_cities_in_Peru + - Street Prefixes: Common knowledge of Peruvian addresses. + """ + + # Define the structure of addresses in Peru + city_formats = ('{{city_name}}',) + street_name_formats = ( + '{{street_prefix}} {{first_name}} {{last_name}}', + '{{street_prefix}} {{last_name}}', + '{{street_prefix}} de {{last_name}}', + ) + street_address_formats = ( + '{{street_name}} #{{building_number}}', + '{{street_name}} #{{building_number}}, {{secondary_address}}', + ) + address_formats = ( + '{{street_address}}\n{{city}}, {{department}}', + ) + + # Postcode format for Peru is 5 digits + postcode_formats = ('#####',) + + # Common secondary address indicators + secondary_address_formats = ( + 'Dpto. ###', 'Of. ###', 'Int. ##', 'Piso #', + ) + + # Common street prefixes in Peru + street_prefixes = ( + 'Avenida', 'Av.', 'Jirón', 'Jr.', 'Calle', 'Paseo', 'Alameda', 'Malecón', + ) + + # Building number formats + building_number_formats = ('###', '####', '##') + + # The 24 Departments of Peru + the Constitutional Province of Callao + departments = ( + 'Amazonas', 'Áncash', 'Apurímac', 'Arequipa', 'Ayacucho', 'Cajamarca', + 'Callao', 'Cusco', 'Huancavelica', 'Huánuco', 'Ica', 'Junín', + 'La Libertad', 'Lambayeque', 'Lima', 'Loreto', 'Madre de Dios', + 'Moquegua', 'Pasco', 'Piura', 'Puno', 'San Martín', 'Tacna', + 'Tumbes', 'Ucayali', + ) + + # A list of major cities in Peru + cities = ( + 'Arequipa', 'Ayacucho', 'Cajamarca', 'Callao', 'Chiclayo', 'Chimbote', + 'Cusco', 'Huancayo', 'Huánuco', 'Huaraz', 'Ica', 'Iquitos', 'Juliaca', + 'Lima', 'Piura', 'Pucallpa', 'Puno', 'Tacna', 'Trujillo', 'Tumbes', + ) + + # Methods to be called by Faker + def department(self) -> str: + """ + Returns a random Peruvian department. + """ + return self.random_element(self.departments) + + def city_name(self) -> str: + """ + Returns a random Peruvian city name. + """ + return self.random_element(self.cities) + + def street_prefix(self) -> str: + """ + Returns a random street prefix. + """ + return self.random_element(self.street_prefixes) + + def secondary_address(self) -> str: + """ + Returns a random secondary address (apartment, office, etc.). + """ + return self.numerify(self.random_element(self.secondary_address_formats)) diff --git a/faker/providers/automotive/es_PE/__init__.py b/faker/providers/automotive/es_PE/__init__.py new file mode 100644 index 00000000000..11adfe85db5 --- /dev/null +++ b/faker/providers/automotive/es_PE/__init__.py @@ -0,0 +1,49 @@ +# faker/providers/automotive/es_PE/__init__.py + +from .. import Provider as AutomotiveProvider + + +class Provider(AutomotiveProvider): + """ + Implement automotive provider for ``es_PE`` locale. + + Sources: + - https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Peru + + .. |license_plate_modern| replace:: + :meth:`license_plate_modern() ` + + .. |license_plate_previous| replace:: + :meth:`license_plate_previous() ` + """ + + # Format from 2010 onwards (most common) + license_format_modern = "???-###" + + # Format from 1995 to 2010 + license_format_previous = "??-####" + + def license_plate_modern(self) -> str: + """ + Generate a modern Peruvian license plate (format: LLL-NNN). + This format has been in use since 2010. + """ + return self.bothify(self.license_format_modern) + + def license_plate_previous(self) -> str: + """ + Generate a previous-generation Peruvian license plate (format: LL-NNNN). + This format was in use from 1995 to 2010. + """ + return self.bothify(self.license_format_previous) + + def license_plate(self) -> str: + """ + Generate a random Peruvian license plate. + + This method randomly chooses between the modern format (75% chance) + and the previous format (25% chance) to generate the result. + """ + if self.generator.random.randint(1, 4) > 1: + return self.license_plate_modern() + return self.license_plate_previous() diff --git a/faker/providers/bank/es_PE/__init__.py b/faker/providers/bank/es_PE/__init__.py new file mode 100644 index 00000000000..c0294e24df4 --- /dev/null +++ b/faker/providers/bank/es_PE/__init__.py @@ -0,0 +1,39 @@ +# faker/providers/bank/es_PE/__init__.py + +from .. import Provider as BankProvider + + +class Provider(BankProvider): + """ + Implement bank provider for ``es_PE`` locale. + + Sources: + - CCI Format: https://www.bcrp.gob.pe/sistema-de-pagos/codigo-de-cuenta-interbancario.html + - Bank Names: https://www.sbs.gob.pe/sistema-financiero-y-de-seguros/sistema-financiero/empresas-en-el-sistema-financiero/ + """ + + # The Peruvian "Código de Cuenta Interbancario" (CCI) has 20 digits. + bban_format = "####################" + country_code = "PE" + + # List of major banks operating in Peru + banks = ( + "Banco de Crédito del Perú (BCP)", + "Interbank", + "Scotiabank Perú", + "BBVA Perú", + "Banco de la Nación", + "MiBanco", + "Banco Pichincha", + "BanBif", + "Banco Ripley", + "Banco Falabella", + "Banco GNB Perú", + "Banco de Comercio", + ) + + def bank(self) -> str: + """ + Returns a random Peruvian bank name. + """ + return self.random_element(self.banks) diff --git a/faker/providers/color/es_PE/__init__.py b/faker/providers/color/es_PE/__init__.py new file mode 100644 index 00000000000..3bcd5c1a218 --- /dev/null +++ b/faker/providers/color/es_PE/__init__.py @@ -0,0 +1,7 @@ +from ..es import Provider as ColorProvider + +localized = True + + +class Provider(ColorProvider): + pass diff --git a/faker/providers/company/es_PE/__init__.py b/faker/providers/company/es_PE/__init__.py new file mode 100644 index 00000000000..0aaf6b7b9b0 --- /dev/null +++ b/faker/providers/company/es_PE/__init__.py @@ -0,0 +1,124 @@ +# faker/providers/company/es_PE/__init__.py + +from collections import OrderedDict + +from .. import Provider as CompanyProvider + + +class Provider(CompanyProvider): + """ + Provider for company names for es_PE locale. + + Company naming scheme and probabilities are inspired by and/or based on + existing companies in Peru. + + Sources: + - https://www.gob.pe/295-tipos-de-empresa + - General knowledge of Peruvian company structures. + """ + + # These formats are generic enough to apply well to Peru. + formats = ( + "{{company_prefix}} {{last_name}} {{company_suffix}}", + "{{company_type}} {{random_company_acronym}} {{company_suffix}}", + "{{company_type}} {{last_name}} {{company_suffix}}", + "{{company_type}} {{random_company_adjective}} {{company_suffix}}", + "{{company_type}} {{last_name}} {{random_name_complements}} {{company_suffix}}", + "{{last_name}} {{random_name_complements}} {{company_suffix}}", + "{{last_name}} y {{last_name}} {{company_suffix}}", + "{{first_name}} {{last_name}} {{last_name}} {{company_suffix}}", + ) + + # Common legal entity types in Peru, with S.A.C. and S.R.L. being very common. + company_suffixes = OrderedDict( + [ + ("S.A.C.", 0.40), # Sociedad Anónima Cerrada + ("S.R.L.", 0.25), # Sociedad de Responsabilidad Limitada + ("S.A.", 0.15), # Sociedad Anónima + ("E.I.R.L.", 0.10),# Empresa Individual de Responsabilidad Limitada + ("S.A.A.", 0.05), # Sociedad Anónima Abierta + ("S.C.", 0.05), # Sociedad Civil + ] + ) + + # Prefixes are largely the same in Spanish-speaking countries. + company_prefixes = ( + "Grupo", + "Hermanos", + "Hnos", + "Familia", + "Corporación", + ) + + # Common business sectors in Peru. + company_types = ( + "Agroindustrial", + "Alimentación", + "Banca", + "Comercial", + "Comercializadora", + "Constructora", + "Consultoría", + "Desarrollo", + "Distribuciones", + "Exportadora", + "Financiera", + "Hotelera", + "Industrias", + "Inmobiliaria", + "Inversiones", + "Logística", + "Manufacturas", + "Minería", + "Pesquera", + "Promotora", + "Restaurante", + "Servicios", + "Soluciones", + "Suministros", + "Supermercados", + "Talleres", + "Tecnología", + "Transportes", + ) + + # Universal complements. + name_complements = ( + "& Asociados", + "y Asociados", + "e Hijos", + ) + + # Adjectives relevant to Peru. + company_adjectives = ( + "Andina", + "Andinos", + "Globales", + "Integrales", + "Internacional", + "Nacional", + "del Norte", + "del Pacífico", + "Peruana", + "Peruanos", + "del Sur", + ) + + def company_type(self) -> str: + return self.random_element(self.company_types) + + def company_suffix(self) -> str: + return self.random_element(self.company_suffixes) + + def random_name_complements(self) -> str: + return self.random_element(self.name_complements) + + def random_company_adjective(self) -> str: + return self.random_element(self.company_adjectives) + + def random_company_acronym(self) -> str: + letters = self.random_letters(self.random_int(2, 4)) + return "".join(letters).upper() + + def company_prefix(self) -> str: + return self.random_element(self.company_prefixes) diff --git a/faker/providers/currency/es_PE/__init__.py b/faker/providers/currency/es_PE/__init__.py new file mode 100644 index 00000000000..b51a712d0da --- /dev/null +++ b/faker/providers/currency/es_PE/__init__.py @@ -0,0 +1,8 @@ +from ..es import Provider as CurrencyProvider + + +class Provider(CurrencyProvider): + price_formats = ["#,##", "%#,##", "%##,##", "%.###,##", "%#.###,##"] + + def pricetag(self) -> str: + return self.numerify(self.random_element(self.price_formats)) diff --git a/faker/providers/date_time/es_PE/__init__.py b/faker/providers/date_time/es_PE/__init__.py new file mode 100644 index 00000000000..8aac30258b0 --- /dev/null +++ b/faker/providers/date_time/es_PE/__init__.py @@ -0,0 +1,5 @@ +from ..es import Provider as DateTimeProvider + + +class Provider(DateTimeProvider): + pass diff --git a/faker/providers/internet/es_PE/__init__.py b/faker/providers/internet/es_PE/__init__.py new file mode 100644 index 00000000000..470a51d28fa --- /dev/null +++ b/faker/providers/internet/es_PE/__init__.py @@ -0,0 +1,27 @@ +from .. import Provider as InternetProvider + +class Provider(InternetProvider): + + # Safe TLDs for generating emails + safe_email_tlds = ("com", "net", "org", "pe") + + # Top-level domains for Peru, with .com and .pe being the most common. + tlds = ("com", "com", "com", "net", "org", "pe", "pe", "pe") + + # Replacements for Spanish accented characters are the same. + # This helps in generating clean user names and domain names. + replacements = ( + ("à", "a"), + ("â", "a"), + ("ã", "a"), + ("á", "a"), + ("ç", "c"), + ("é", "e"), + ("ê", "e"), + ("í", "i"), + ("ô", "o"), + ("ö", "o"), + ("õ", "o"), + ("ó", "o"), + ("ú", "u"), + ) diff --git a/faker/providers/job/es_PE/__init__.py b/faker/providers/job/es_PE/__init__.py new file mode 100644 index 00000000000..91208608034 --- /dev/null +++ b/faker/providers/job/es_PE/__init__.py @@ -0,0 +1,5 @@ +from ..es import Provider as BaseProvider + + +class Provider(BaseProvider): + pass diff --git a/faker/providers/person/es_PE/__init__.py b/faker/providers/person/es_PE/__init__.py new file mode 100644 index 00000000000..03a6fd05bb9 --- /dev/null +++ b/faker/providers/person/es_PE/__init__.py @@ -0,0 +1,102 @@ +from typing import Tuple + +from ..es import Provider as PersonProvider + + +class Provider(PersonProvider): + # The name format in Peru is the same as in Spain: + # First Name(s) + Paternal Last Name + Maternal Last Name. + # The formats from the es_ES provider are a good fit. + formats_male: Tuple[str, ...] = ( + "{{first_name_male}} {{last_name}} {{last_name}}", + "{{first_name_male}} {{last_name}} {{last_name}}", + "{{first_name_male}} {{last_name}} {{last_name}}", + "{{first_name_male}} {{last_name}}", + "{{first_name_male}} {{last_name}}-{{last_name}}", + "{{first_name_male}} {{first_name_male}} {{last_name}} {{last_name}}", + ) + + formats_female: Tuple[str, ...] = ( + "{{first_name_female}} {{last_name}} {{last_name}}", + "{{first_name_female}} {{last_name}} {{last_name}}", + "{{first_name_female}} {{last_name}} {{last_name}}", + "{{first_name_female}} {{last_name}}", + "{{first_name_female}} {{last_name}}-{{last_name}}", + "{{first_name_female}} {{first_name_female}} {{last_name}} {{last_name}}", + ) + + formats: Tuple[str, ...] = formats_male + formats_female + + # First names from es_ES provider, with common Peruvian names added. + first_names_male: Tuple[str, ...] = PersonProvider.first_names_male + ( + "André", + "Brayan", + "Claudio", + "Dayron", + "Deyvis", + "Edison", + "Gianluca", + "Jefferson", + "Jhair", + "Joao", + "Kevin", + "Liam", + "Paolo", + "Renzo", + "Thiago", + "Yoshimar", + "Yordy", + ) + + first_names_female: Tuple[str, ...] = PersonProvider.first_names_female + ( + "Almendra", + "Antonella", + "Briana", + "Brunella", + "Camila", + "Danna", + "Dayana", + "Daysi", + "Fiorella", + "Gianella", + "Kiara", + "Luciana", + "Micaela", + "Pierina", + "Valeska", + "Ximena", + "Yahaira", + "Yajaira", + ) + + first_names: Tuple[str, ...] = first_names_male + first_names_female + + # Last names from es_ES provider, with common Peruvian last names added. + # Includes names of Quechua and Aymara origin. + last_names: Tuple[str, ...] = PersonProvider.last_names + ( + "Apaza", + "Cahuana", + "Ccanto", + "Chagua", + "Chambi", + "Charca", + "Chavez", + "Chávez", + "Choque", + "Condori", + "Guerrero", + "Huaman", + "Huamán", + "Huaraca", + "Inca", + "Mamani", + "Paucar", + "Quispe", + "Sánchez", + "Tito", + "Vargas", + "Yucra", + ) + + # Prefixes are the same. + prefixes: Tuple[str, ...] = ("de", "del") diff --git a/faker/providers/phone_number/es_PE/__init__.py b/faker/providers/phone_number/es_PE/__init__.py new file mode 100644 index 00000000000..c80d23b73c3 --- /dev/null +++ b/faker/providers/phone_number/es_PE/__init__.py @@ -0,0 +1,90 @@ +from .. import Provider as PhoneNumberProvider + + +class Provider(PhoneNumberProvider): + """ + Sources: + - https://en.wikipedia.org/wiki/Telephone_numbers_in_Peru + - https://www.mtc.gob.pe/comunicaciones/telefonia_fija.html + """ + + # Mobile numbers in Peru are 9 digits long and always start with 9. + mobile_formats = ( + # Local format + "9## ### ###", + "9########", + # International format + "+51 9## ### ###", + ) + + # Landline numbers have a 1-digit area code for Lima/Callao (1) + # and a 2-digit area code for other departments. + # The total length is 9 digits including the area code. + lima_landline_formats = ( + # Local format + "(1) ###-####", + "1#######", + # International format + "+51 1 ###-####", + ) + + # A selection of major provincial area codes. + province_area_codes = ( + "41", # Amazonas + "43", # Áncash + "83", # Apurímac + "54", # Arequipa + "66", # Ayacucho + "76", # Cajamarca + "84", # Cusco + "62", # Huánuco + "56", # Ica + "64", # Junín + "44", # La Libertad + "74", # Lambayeque + "65", # Loreto + "73", # Piura + "51", # Puno + "52", # Tacna + ) + + # Formats for provincial landlines. + province_landline_formats = ( + # Local format + "({}) ###-###", + "{}######", + # International format + "+51 {} ###-###", + ) + + def mobile_number(self) -> str: + """ + Returns a random 9-digit mobile phone number. + """ + pattern = self.random_element(self.mobile_formats) + return self.numerify(pattern) + + def landline_number(self) -> str: + """ + Returns a random 9-digit landline phone number, which can be + from Lima or another province. + """ + # Give Lima a higher chance as it has the largest population. + if self.generator.random.randint(1, 3) == 1: + # Generate a Lima number + pattern = self.random_element(self.lima_landline_formats) + return self.numerify(pattern) + else: + # Generate a provincial number + area_code = self.random_element(self.province_area_codes) + pattern = self.random_element(self.province_landline_formats).format(area_code) + return self.numerify(pattern) + + def phone_number(self) -> str: + """ + Returns a random phone number, with a higher probability of it + being a mobile number. + """ + if self.generator.random.randint(1, 4) > 1: + return self.mobile_number() + return self.landline_number() diff --git a/faker/providers/ssn/es_PE/__init__.py b/faker/providers/ssn/es_PE/__init__.py new file mode 100644 index 00000000000..604a97161a1 --- /dev/null +++ b/faker/providers/ssn/es_PE/__init__.py @@ -0,0 +1,60 @@ +import random + +from .. import Provider as BaseProvider + + +class Provider(BaseProvider): + """ + A Faker provider for Peruvian identity numbers. + """ + + def dni(self) -> str: + """ + Generates a random DNI (Documento Nacional de Identidad). + It's an 8-digit number. + :return: A random 8-digit DNI. + """ + return self.numerify("########") + + def ruc(self) -> str: + """ + Generates a valid RUC (Registro Único de Contribuyentes). + It's an 11-digit number. + https://www.gob.pe/729-consultar-el-estado-del-ruc + :return: A random 11-digit RUC. + """ + # RUC can start with 10 (person), 15/17 (non-domiciled), or 20 (company) + # We will generate the most common ones: 10 and 20. + ruc_type = self.random_element(["10", "20"]) + if ruc_type == "10": + # For natural persons, the next 8 digits are often the DNI + body = self.numerify("########") + else: # ruc_type == '20' + # For companies, the next 8 digits are correlative + body = self.numerify("########") + + base_ruc = f"{ruc_type}{body}" + check_digit = self._calculate_ruc_check_digit(base_ruc) + return f"{base_ruc}{check_digit}" + + @staticmethod + def _calculate_ruc_check_digit(base_ruc: str) -> int: + """ + Calculates the check digit for a Peruvian RUC. + The algorithm is weighted sum modulo 11 (Módulo 11). + """ + factors = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] + total = sum(int(digit) * factor for digit, factor in zip(base_ruc, factors)) + remainder = total % 11 + check_digit = 11 - remainder + if check_digit == 10: + return 0 + if check_digit == 11: + return 1 + return check_digit + + def ssn(self) -> str: + """ + Alias for DNI. + """ + return self.dni() \ No newline at end of file diff --git a/tests/providers/test_address.py b/tests/providers/test_address.py index cee16b6667e..98115f52826 100644 --- a/tests/providers/test_address.py +++ b/tests/providers/test_address.py @@ -25,6 +25,7 @@ from faker.providers.address.en_US import Provider as EnUsAddressProvider from faker.providers.address.es_CO import Provider as EsCoAddressProvider from faker.providers.address.es_ES import Provider as EsEsAddressProvider +from faker.providers.address.es_PE import Provider as EsPeAddressProvider from faker.providers.address.es_MX import Provider as EsMxAddressProvider from faker.providers.address.fa_IR import Provider as FaIrAddressProvider from faker.providers.address.fi_FI import Provider as FiFiAddressProvider @@ -2648,3 +2649,59 @@ def test_administrative_unit(self, faker, num_samples): administrative_unit = faker.administrative_unit() assert isinstance(administrative_unit, str) assert administrative_unit in ZuZaAddressProvider.provinces + +class TestEsPE: + """Unit tests for the es_PE provider.""" + + def test_postcode(self, faker, num_samples): + for _ in range(num_samples): + postcode = faker.postcode() + assert isinstance(postcode, str) + assert re.fullmatch(r"\d{4}", postcode) + + def test_city_name(self, faker, num_samples): + for _ in range(num_samples): + city_name = faker.city_name() + assert isinstance(city_name, str) + assert city_name in EsPeAddressProvider.cities + + def test_city_suffix(self, faker, num_samples): + for _ in range(num_samples): + city_suffix = faker.city_suffix() + assert isinstance(city_suffix, str) + assert city_suffix in EsPeAddressProvider.city_suffixes + + def test_city(self, faker, num_samples): + for _ in range(num_samples): + city = faker.city() + assert isinstance(city, str) + assert city in EsPeAddressProvider.cities + + def test_country(self, faker, num_samples): + for _ in range(num_samples): + country = faker.country() + assert isinstance(country, str) + assert country in EsPeAddressProvider.countries + + def test_street_name(self, faker, num_samples): + for _ in range(num_samples): + street_name = faker.street_name() + assert isinstance(street_name, str) + assert street_name in EsPeAddressProvider.street_names + + def test_address(self, faker, num_samples): + for _ in range(num_samples): + address = faker.address() + assert isinstance(address, str) + + def test_province(self, faker, num_samples): + for _ in range(num_samples): + province = faker.province() + assert isinstance(province, str) + assert province in EsPeAddressProvider.provinces + + def test_administrative_unit(self, faker, num_samples): + for _ in range(num_samples): + administrative_unit = faker.administrative_unit() + assert isinstance(administrative_unit, str) + assert administrative_unit in EsPeAddressProvider.provinces diff --git a/tests/providers/test_automotive.py b/tests/providers/test_automotive.py index 618593dc7f7..81345b13e6a 100644 --- a/tests/providers/test_automotive.py +++ b/tests/providers/test_automotive.py @@ -7,6 +7,7 @@ from faker.providers.automotive.de_AT import Provider as DeAtAutomotiveProvider from faker.providers.automotive.de_DE import Provider as DeDeAutomotiveProvider from faker.providers.automotive.es_ES import Provider as EsEsAutomotiveProvider +from faker.providers.automotive.es_PE import Provider as EsPeAutomotiveProvider from faker.providers.automotive.ro_RO import Provider as RoRoAutomotiveProvider from faker.providers.automotive.ru_RU import Provider as RuRuAutomotiveProvider from faker.providers.automotive.sk_SK import Provider as SkSkAutomotiveProvider @@ -414,3 +415,14 @@ class TestZhTw(_SimpleAutomotiveTestMixin): r"([A-Z]{3}-\d{4})|" # new format since 2014 r"([A-Z]{3}-\d{3})", # commercial cars since 2012 ) + +class TestEsPE: + def test_automotive_methods(self): + """Test automotive methods""" + # Test a few times to check for both modern and previous formats + for _ in range(10): + plate = self.factory.license_plate() + self.assertTrue( + re.match(r"^[A-Z]{3}-\d{3}$", plate) or re.match(r"^[A-Z]{2}-\d{4}$", plate), + f"Plate {plate} did not match expected formats.", + ) diff --git a/tests/providers/test_bank.py b/tests/providers/test_bank.py index 60fdd4a6e04..b7a4e0b26f5 100644 --- a/tests/providers/test_bank.py +++ b/tests/providers/test_bank.py @@ -12,6 +12,7 @@ from faker.providers.bank.en_PH import Provider as EnPhBankProvider from faker.providers.bank.es_AR import Provider as EsArBankProvider from faker.providers.bank.es_ES import Provider as EsEsBankProvider +from faker.providers.bank.es_PE import Provider as EsPeBankProvider from faker.providers.bank.es_MX import Provider as EsMxBankProvider from faker.providers.bank.es_MX import is_valid_clabe from faker.providers.bank.fi_FI import Provider as FiFiBankProvider @@ -516,3 +517,17 @@ def test_bank_not_implemented_error(self, faker): with pytest.raises(AttributeError): provider.bank() + + +class TestEsPE: + def test_bank_methods(self): + """Test bank methods""" + bban = self.factory.bban() + self.assertIsInstance(bban, str) + self.assertEqual(len(bban), 20) + self.assertTrue(bban.isdigit()) + + # BIC is not localized, but should return a valid format + bic = self.factory.bic() + self.assertIsInstance(bic, str) + self.assertTrue(8 <= len(bic) <= 11) diff --git a/tests/providers/test_color.py b/tests/providers/test_color.py index 53b0442dca5..05b8e822b63 100644 --- a/tests/providers/test_color.py +++ b/tests/providers/test_color.py @@ -15,6 +15,7 @@ from faker.providers.color.de_DE import Provider as DeDeColorProvider from faker.providers.color.el_GR import Provider as ElGrColorProvider from faker.providers.color.es_ES import Provider as EsEsColorProvider +from faker.providers.color.es_PE import Provider as EsPeColorProvider from faker.providers.color.fa_IR import Provider as FaIrColorProvider from faker.providers.color.he_IL import Provider as HeILColorProvider from faker.providers.color.hy_AM import Provider as HyAmColorProvider @@ -528,3 +529,19 @@ def test_safe_color_name(self, faker, num_samples): safe_color_name = faker.safe_color_name() assert isinstance(safe_color_name, str) assert safe_color_name in UzUzColorProvider.safe_colors + + +class TestEsPE: + """Test uz_UZ color provider methods""" + + def test_color_name(self, faker, num_samples): + for _ in range(num_samples): + color_name = faker.color_name() + assert isinstance(color_name, str) + assert color_name in EsPeColorProvider.all_colors.keys() + + def test_safe_color_name(self, faker, num_samples): + for _ in range(num_samples): + safe_color_name = faker.safe_color_name() + assert isinstance(safe_color_name, str) + assert safe_color_name in EsPeColorProvider.safe_colors diff --git a/tests/providers/test_company.py b/tests/providers/test_company.py index 09d4981dfa3..8373e0920fc 100644 --- a/tests/providers/test_company.py +++ b/tests/providers/test_company.py @@ -12,6 +12,7 @@ from faker.providers.company.el_GR import Provider as ElGrCompanyProvider from faker.providers.company.en_PH import Provider as EnPhCompanyProvider from faker.providers.company.es_ES import Provider as EsEsCompanyProvider +from faker.providers.company.es_PE import Provider as EsPeCompanyProvider from faker.providers.company.fil_PH import Provider as FilPhCompanyProvider from faker.providers.company.hu_HU import Provider as HuHuCompanyProvider from faker.providers.company.hy_AM import Provider as HyAmCompanyProvider @@ -620,3 +621,18 @@ def test_company(self, faker, num_samples): for _ in range(num_samples): company = faker.company() assert isinstance(company, str) + + +class TestEsPe: + """Test esE_ES company provider methods""" + + def test_company_suffix(self, faker, num_samples): + for _ in range(num_samples): + suffix = faker.company_suffix() + assert isinstance(suffix, str) + assert suffix in list(EsPeCompanyProvider.company_suffixes.keys()) + + def test_company(self, faker, num_samples): + for _ in range(num_samples): + company = faker.company() + assert isinstance(company, str) diff --git a/tests/providers/test_currency.py b/tests/providers/test_currency.py index b9ca42c40fc..bbee7637922 100644 --- a/tests/providers/test_currency.py +++ b/tests/providers/test_currency.py @@ -323,6 +323,35 @@ def test_pricetag(self, faker, num_samples): assert isinstance(pricetag, str) +class TestEsPe: + """Test es_PE currency provider""" + + num_samples = 100 + + @classmethod + def setup_class(cls): + from faker.providers.currency.es_PE import Provider as EsPeCurrencyProvider + + cls.provider = EsPeCurrencyProvider + cls.currencies = cls.provider.currencies + cls.currency_codes, cls.currency_names = tuple(zip(*cls.currencies)) + + def test_currency(self, faker, num_samples): + for _ in range(num_samples): + cur = faker.currency() + assert cur in self.currencies + + def test_currency_name(self, faker, num_samples): + for _ in range(num_samples): + name = faker.currency_name() + assert name in self.currency_names + + def test_pricetag(self, faker, num_samples): + for _ in range(num_samples): + pricetag = faker.pricetag() + assert isinstance(pricetag, str) + + class TestFaIr: """Test fa_IR currency provider""" diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py index 58152b67528..a68393b9153 100644 --- a/tests/providers/test_date_time.py +++ b/tests/providers/test_date_time.py @@ -26,6 +26,7 @@ from faker.providers.date_time.de_DE import Provider as DeDeProvider from faker.providers.date_time.el_GR import Provider as ElGrProvider from faker.providers.date_time.es_ES import Provider as EsEsProvider +from faker.providers.date_time.es_PE import Provider as EsPeProvider from faker.providers.date_time.fr_DZ import Provider as FrDzProvider from faker.providers.date_time.fr_FR import Provider as FrFrProvider from faker.providers.date_time.gu_IN import Provider as GuINProvider @@ -1421,3 +1422,16 @@ def test_month(self): month = self.fake.month_name() assert isinstance(month, str) assert month in UzUzProvider.MONTH_NAMES.values() + +class TestEsPe: + def setUp(self): + self.fake = Faker("es_PE") + Faker.seed(0) + + def test_day(self): + day = self.fake.day_of_week() + assert day in EsPeProvider.DAY_NAMES.values() + + def test_month(self): + month = self.fake.month_name() + assert month in EsPeProvider.MONTH_NAMES.values() diff --git a/tests/providers/test_internet.py b/tests/providers/test_internet.py index 4e0ecc6914d..4b0f4a9a388 100644 --- a/tests/providers/test_internet.py +++ b/tests/providers/test_internet.py @@ -14,6 +14,7 @@ from faker.providers.internet.az_AZ import Provider as AzAzInternetProvider from faker.providers.internet.en_GB import Provider as EnGbInternetProvider from faker.providers.internet.es_ES import Provider as EsEsInternetProvider +from faker.providers.internet.es_PE import Provider as EsPeInternetProvider from faker.providers.internet.pl_PL import Provider as PlPlInternetProvider from faker.providers.internet.ro_RO import Provider as RoRoInternetProvider from faker.providers.internet.ru_RU import Provider as RuRuInternetProvider @@ -973,3 +974,15 @@ def test_slug(self, faker): num_of_samples = 100 for _ in range(num_of_samples): assert faker.slug() != "" + +class TestEsPe: + """Test Es_Pe internet provider methods""" + + def test_tld(self, faker): + tld = faker.tld() + assert tld in EsPeInternetProvider.tlds + + def test_slug(self, faker): + num_of_samples = 100 + for _ in range(num_of_samples): + assert faker.slug() != "" \ No newline at end of file diff --git a/tests/providers/test_job.py b/tests/providers/test_job.py index b405e324a42..41dba15ede9 100644 --- a/tests/providers/test_job.py +++ b/tests/providers/test_job.py @@ -5,6 +5,7 @@ from faker.providers.job.de_DE import Provider as DeDeJobProvider from faker.providers.job.el_GR import Provider as ElGrJobProvider from faker.providers.job.es_ES import Provider as EsEsJobProvider +from faker.providers.job.es_PE import Provider as EsPeJobProvider from faker.providers.job.fr_FR import Provider as FrFrJobProvider from faker.providers.job.hu_HU import Provider as HuHuJobProvider from faker.providers.job.hy_AM import Provider as HyAmJobProvider @@ -185,3 +186,10 @@ def test_job(self, faker, num_samples): job = faker.job() assert isinstance(job, str) assert job in ViVNJobProvider.jobs + +class TestEsPe: + """Test es_PE job provider""" + + def test_job(self, faker, num_samples): + for _ in range(num_samples): + assert faker.job() in EsPeJobProvider.jobs diff --git a/tests/providers/test_person.py b/tests/providers/test_person.py index 44822ed07d4..3e7c54c63f4 100644 --- a/tests/providers/test_person.py +++ b/tests/providers/test_person.py @@ -20,6 +20,7 @@ from faker.providers.person.en_PK import Provider as EnPKprovider from faker.providers.person.en_US import Provider as EnUSProvider from faker.providers.person.es import Provider as EsProvider +from faker.providers.person.es_PE import Provider as EsPEProvider from faker.providers.person.es_CO import Provider as EsCOProvider from faker.providers.person.et_EE import Provider as EtEEProvider from faker.providers.person.fi_FI import Provider as FiProvider @@ -2403,6 +2404,32 @@ def test_name_formats(self): self.assertIsInstance(female_name, str) self.assertGreaterEqual(len(female_name.split()), 2) +class TestEsPe: + def setUp(self): + self.fake = Faker("es_PE") + Faker.seed(0) + self.provider = EsPEProvider + + def test_male_first_names(self): + for _ in range(100): + res = self.fake.first_name_male() + assert res in self.provider.first_names_male + + def test_female_first_names(self): + for _ in range(100): + res = self.fake.first_name_female() + assert res in self.provider.first_names_female + + def test_male_last_names(self): + for _ in range(100): + res = self.fake.last_name_male() + assert res in self.provider.last_names_male + + def test_female_last_names(self): + for _ in range(100): + res = self.fake.last_name_female() + assert res in self.provider.last_names_female + if __name__ == "__main__": unittest.main() diff --git a/tests/providers/test_phone_number.py b/tests/providers/test_phone_number.py index bfd36034657..c7b3920a3c4 100644 --- a/tests/providers/test_phone_number.py +++ b/tests/providers/test_phone_number.py @@ -6,6 +6,7 @@ from faker.providers.phone_number.de_AT import Provider as DeAtPhoneNumberProvider from faker.providers.phone_number.de_CH import Provider as DeChPhoneNumberProvider from faker.providers.phone_number.en_PH import Provider as EnPhPhoneNumberProvider +from faker.providers.phone_number.ee_PE import Provider as EsPePhoneNumberProvider class TestPhoneNumber: @@ -555,3 +556,18 @@ def test_phone_number(self, faker, num_samples): phone_number = faker.phone_number() assert isinstance(phone_number, str) assert pattern.fullmatch(phone_number) + +class TestEsPe: + def test_phone_number(self): + """Test phone number methods""" + for _ in range(10): + phone_number = self.factory.phone_number() + self.assertIsInstance(phone_number, str) + # Check for mobile: 9XX XXX XXX or landline (Lima): (1) XXX XXXX or (1) XXX-XXXX + # or landline (province): (XX) XXX XXXX or (XX) XXX-XXXX + self.assertTrue( + re.match(r"^9\d{2} \d{3} \d{3}$", phone_number) + or re.match(r"^\(1\) \d{3}[- ]?\d{4}$", phone_number) + or re.match(r"^\(\d{2}\) \d{3}[- ]?\d{4}$", phone_number), + f"Phone number {phone_number} did not match expected formats.", + ) diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py index 85b47b0d371..b3c772f8f77 100644 --- a/tests/providers/test_ssn.py +++ b/tests/providers/test_ssn.py @@ -17,6 +17,8 @@ from faker import Factory, Faker from faker.providers.ssn.el_GR import tin_checksum as gr_tin_checksum from faker.providers.ssn.en_CA import checksum as ca_checksum +from faker.providers.ssn.es_PE import checksum as pe_checksum +from faker.providers.ssn.es_PE import Provider as pe_Provider from faker.providers.ssn.es_CL import rut_check_digit as cl_rut_checksum from faker.providers.ssn.es_CO import nit_check_digit from faker.providers.ssn.es_MX import curp_checksum as mx_curp_checksum @@ -1444,3 +1446,36 @@ def test_incorrect_birthday(self): def test_incorrect_gender(self): with pytest.raises(ValueError): self.fake.ssn(gender="f") + +class TestEsPe: + def test_ssn_methods(self): + """Test SSN (DNI and RUC) methods""" + # DNI + dni = self.factory.dni() + self.assertIsInstance(dni, str) + self.assertEqual(len(dni), 8) + self.assertTrue(dni.isdigit()) + + # ssn is an alias for dni + ssn = self.factory.ssn() + self.assertEqual(dni, ssn) + + # RUC + ruc = self.factory.ruc() + self.assertIsInstance(ruc, str) + self.assertEqual(len(ruc), 11) + self.assertTrue(ruc.isdigit()) + self.assertIn(ruc[:2], ["10", "20"]) + + # Validate RUC check digit + base_ruc = ruc[:-1] + check_digit = int(ruc[-1]) + factors = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] + total = sum(int(digit) * factor for digit, factor in zip(base_ruc, factors)) + remainder = total % 11 + expected_check_digit = 11 - remainder + if expected_check_digit == 10: + expected_check_digit = 0 + elif expected_check_digit == 11: + expected_check_digit = 1 + self.assertEqual(check_digit, expected_check_digit)