use rand::thread_rng;
use idsmith::personal_id::date::Gender;
use idsmith::personal_id::{self, GenOptions};
const ALL_SPECIFIC_COUNTRIES: &[&str] = &[
"AT", "BA", "BE", "BG", "CH", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GB", "GR", "HR", "IE",
"IS", "IT", "LT", "LV", "ME", "NL", "NO", "PL", "PT", "RO", "RS", "SE", "SI", "SK", "TR",
"US", "CA", "BR", "AR", "CL", "CO", "CU", "DO", "UY", "EC", "PE", "MX",
"CN", "IN", "JP", "KR", "TW", "TH", "SG", "MY", "ID", "HK", "AU", "NZ",
"ZA", "IL", "EG", "DZ", "MU", "PK", "SA", "KZ", "UA", "LU",
];
#[test]
fn test_id_country_count() {
let registry = personal_id::Registry::new();
let countries = registry.list_countries();
assert!(
countries.len() >= 90,
"expected >= 90 personal ID countries, got {}",
countries.len()
);
}
#[test]
fn test_all_specific_countries_generate_valid() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
for &cc in ALL_SPECIFIC_COUNTRIES {
for _ in 0..20 {
let code = registry
.generate(cc, &opts, &mut rng)
.unwrap_or_else(|| panic!("{}: generate returned None", cc));
let valid = registry.validate(cc, &code);
assert_eq!(valid, Some(true), "{}: validation failed for {}", cc, code);
}
}
}
#[test]
fn test_all_specific_countries_parse() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
for &cc in ALL_SPECIFIC_COUNTRIES {
let code = registry.generate(cc, &opts, &mut rng).unwrap();
let parsed = registry
.parse(cc, &code)
.unwrap_or_else(|| panic!("{}: parse returned None for {}", cc, code));
assert!(parsed.valid, "{}: parsed.valid is false for {}", cc, code);
assert!(!parsed.code.is_empty());
}
}
#[test]
fn test_all_listed_countries_generate_valid() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
let all = registry.list_countries();
for (cc, _name, _id_name) in &all {
for _ in 0..5 {
let code = registry
.generate(cc, &opts, &mut rng)
.unwrap_or_else(|| panic!("{}: generate returned None", cc));
let valid = registry.validate(cc, &code);
assert_eq!(valid, Some(true), "{}: validation failed for {}", cc, code);
}
}
}
#[test]
fn test_territory_aliases() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
let aliases = &["PR", "GU", "CC", "CX", "CK", "BL", "SH", "BV"];
for &cc in aliases {
assert!(registry.is_supported(cc), "{} should be supported", cc);
let code = registry
.generate(cc, &opts, &mut rng)
.unwrap_or_else(|| panic!("{}: generate returned None", cc));
let valid = registry.validate(cc, &code);
assert_eq!(valid, Some(true), "{}: validation failed for {}", cc, code);
}
}
#[test]
fn test_gender_filter() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let gendered_countries = &[
"EE", "FI", "SE", "NO", "PL", "RO", "BG", "CZ", "SK", "BE", "FR", "IT",
"AR", "MX", "CN", "KR", "TW", "ZA", "EG", "MY", "ID", "KZ", "UA",
];
for &cc in gendered_countries {
let opts_m = GenOptions {
gender: Some(Gender::Male),
year: None,
};
let opts_f = GenOptions {
gender: Some(Gender::Female),
year: None,
};
for _ in 0..5 {
let code_m = registry.generate(cc, &opts_m, &mut rng).unwrap();
let parsed_m = registry.parse(cc, &code_m).unwrap();
if let Some(ref g) = parsed_m.gender {
assert_eq!(g, "male", "{}: expected male, got {} for {}", cc, g, code_m);
}
let code_f = registry.generate(cc, &opts_f, &mut rng).unwrap();
let parsed_f = registry.parse(cc, &code_f).unwrap();
if let Some(ref g) = parsed_f.gender {
assert_eq!(
g, "female",
"{}: expected female, got {} for {}",
cc, g, code_f
);
}
}
}
}
#[test]
fn test_year_filter() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let year_countries = &[
"EE", "FI", "SE", "NO", "PL", "RO", "BG", "DK", "CN", "KR", "ZA", "EG", "MX", "MY", "ID", "KZ", "UA", "LU",
];
for &cc in year_countries {
let opts = GenOptions {
gender: None,
year: Some(1985),
};
for _ in 0..5 {
let code = registry.generate(cc, &opts, &mut rng).unwrap();
let parsed = registry.parse(cc, &code).unwrap();
if let Some(ref dob) = parsed.dob {
assert!(
dob.starts_with("1985-"),
"{}: expected year 1985 in dob, got {} for {}",
cc,
dob,
code
);
}
}
}
}
#[test]
fn test_unsupported_country() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
assert!(registry.generate("XX", &opts, &mut rng).is_none());
assert!(registry.validate("XX", "12345").is_none());
assert!(registry.parse("XX", "12345").is_none());
assert!(registry.name("XX").is_none());
}
#[test]
fn test_registry_name() {
let registry = personal_id::Registry::new();
assert_eq!(registry.name("EE"), Some("Isikukood"));
assert_eq!(registry.name("IT"), Some("Codice Fiscale"));
assert_eq!(registry.name("PL"), Some("PESEL"));
assert_eq!(registry.name("GB"), Some("NINO"));
assert_eq!(registry.name("US"), Some("SSN"));
assert_eq!(registry.name("CN"), Some("Resident ID"));
assert_eq!(registry.name("ZA"), Some("SA ID"));
assert_eq!(registry.name("IN"), Some("Aadhaar"));
assert_eq!(registry.name("CU"), Some("NI"));
assert_eq!(registry.name("DO"), Some("Cedula"));
assert_eq!(registry.name("MU"), Some("NID"));
assert_eq!(registry.name("PK"), Some("CNIC"));
assert_eq!(registry.name("PR"), Some("SSN"));
}
#[test]
fn test_checksum_corruption() {
let registry = personal_id::Registry::new();
let mut rng = thread_rng();
let opts = GenOptions::default();
let checksum_countries = &[
"BR", "CN", "IN", "ZA", "IL", "CA", "JP", "TH", "AU", "KZ", "UA", "SA", "LU",
];
for &cc in checksum_countries {
for _ in 0..10 {
let code = registry.generate(cc, &opts, &mut rng).unwrap();
assert_eq!(
registry.validate(cc, &code),
Some(true),
"{}: valid code failed",
cc
);
let mut chars: Vec<char> = code.chars().collect();
if let Some(pos) = chars.iter().position(|c| c.is_ascii_digit()) {
let old = chars[pos];
chars[pos] = if old == '9' {
'0'
} else {
(old as u8 + 1) as char
};
let corrupted: String = chars.into_iter().collect();
let valid = registry.validate(cc, &corrupted).unwrap_or(true);
let _ = valid;
}
}
}
}