#![expect(
clippy::arithmetic_side_effects,
clippy::string_slice,
clippy::uninlined_format_args,
reason = "Pre-existing validation property test debt moved during module collapse; cleanup is separate from this behavior-preserving change."
)]
use super::super::{
Issue, Severity, is_coded_value, is_date, is_email, is_identifier, is_numeric, is_person_name,
is_phone_number, is_sequence_id, is_ssn, is_time, is_timestamp, is_valid_birth_date,
is_within_range, validate_checksum, validate_data_type, validate_luhn_checksum,
validate_mathematical_relationship,
};
use proptest::prelude::*;
fn valid_date_strategy() -> impl Strategy<Value = String> {
"[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])"
}
fn valid_time_strategy() -> impl Strategy<Value = String> {
prop_oneof![
"[0-2][0-9][0-5][0-9]".prop_filter("Valid hour 0-23", |s| {
let hour: u32 = s[0..2].parse().unwrap_or(24);
hour <= 23
}),
"[0-2][0-9][0-5][0-9][0-5][0-9]".prop_filter("Valid hour 0-23", |s| {
let hour: u32 = s[0..2].parse().unwrap_or(24);
hour <= 23
}),
]
}
fn valid_timestamp_strategy() -> impl Strategy<Value = String> {
"[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-2][0-9][0-5][0-9][0-5][0-9]".prop_filter(
"Valid timestamp",
|s| {
let hour: u32 = s[8..10].parse().unwrap_or(24);
hour <= 23
},
)
}
fn numeric_strategy() -> impl Strategy<Value = String> {
prop_oneof![
"-?[0-9]+".prop_filter("Non-empty", |s| !s.is_empty() && s != "-" && s != "+"),
"-?[0-9]+\\.[0-9]+",
]
}
fn identifier_strategy() -> impl Strategy<Value = String> {
"[a-zA-Z0-9_\\-\\.]+"
}
fn person_name_strategy() -> impl Strategy<Value = String> {
"[a-zA-Z\\s\\-']+"
}
#[expect(
dead_code,
reason = "Strategy helper is retained with the copied validation property test suite."
)]
fn ascii_string_strategy() -> impl Strategy<Value = String> {
".*"
}
fn valid_email_strategy() -> impl Strategy<Value = String> {
"[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]{2,}"
}
fn valid_phone_strategy() -> impl Strategy<Value = String> {
prop_oneof!["[0-9]{7}", "[0-9]{10}", "[0-9]{11}", "[0-9]{15}",]
}
fn valid_ssn_strategy() -> impl Strategy<Value = String> {
(
"(00[1-9]|0[1-9][0-9]|[1-5][0-9]{2}|6[0-5][0-9]|66[0-5]|66[7-9]|6[7-9][0-9]|[78][0-9]{2})",
"(0[1-9]|[1-9][0-9])",
"(000[1-9]|00[1-9][0-9]|0[1-9][0-9]{2}|[1-9][0-9]{3})",
)
.prop_map(|(area, group, serial)| format!("{}{}{}", area, group, serial))
}
fn valid_luhn_strategy() -> impl Strategy<Value = String> {
prop_oneof![
Just("4532015112830366".to_string()), Just("79927398713".to_string()), Just("4242424242424242".to_string()), ]
}
proptest! {
#[test]
fn test_validate_data_type_never_panics(value in "[0-9a-zA-Z\\s\\-\\.@]*", datatype in "[A-Z]{2,3}") {
let _ = validate_data_type(&value, &datatype);
}
}
proptest! {
#[test]
fn test_is_date_never_panics(value in ".*") {
let _ = is_date(&value);
}
}
proptest! {
#[test]
fn test_is_time_never_panics(value in ".*") {
let _ = is_time(&value);
}
}
proptest! {
#[test]
fn test_is_timestamp_never_panics(value in ".*") {
let _ = is_timestamp(&value);
}
}
proptest! {
#[test]
fn test_is_numeric_never_panics(value in ".*") {
let _ = is_numeric(&value);
}
}
proptest! {
#[test]
fn test_is_identifier_never_panics(value in ".*") {
let _ = is_identifier(&value);
}
}
proptest! {
#[test]
fn test_is_person_name_never_panics(value in ".*") {
let _ = is_person_name(&value);
}
}
proptest! {
#[test]
fn test_is_email_never_panics(value in ".*") {
let _ = is_email(&value);
}
}
proptest! {
#[test]
fn test_is_phone_number_never_panics(value in ".*") {
let _ = is_phone_number(&value);
}
}
proptest! {
#[test]
fn test_is_ssn_never_panics(value in ".*") {
let _ = is_ssn(&value);
}
}
proptest! {
#[test]
fn test_validate_luhn_never_panics(value in ".*") {
let _ = validate_luhn_checksum(&value);
}
}
proptest! {
#[test]
fn test_validate_checksum_never_panics(value in ".*", algorithm in ".*") {
let _ = validate_checksum(&value, &algorithm);
}
}
proptest! {
#[test]
fn test_is_within_range_never_panics(value in ".*", min in ".*", max in ".*") {
let _ = is_within_range(&value, &min, &max);
}
}
proptest! {
#[test]
fn test_validate_mathematical_relationship_never_panics(
v1 in ".*", v2 in ".*", op in ".*"
) {
let _ = validate_mathematical_relationship(&v1, &v2, &op);
}
}
proptest! {
#[test]
fn test_valid_dates_are_recognized(date in valid_date_strategy()) {
prop_assert!(is_date(&date));
}
}
proptest! {
#[test]
fn test_valid_times_are_recognized(time in valid_time_strategy()) {
prop_assert!(is_time(&time));
}
}
proptest! {
#[test]
fn test_valid_timestamps_are_recognized(ts in valid_timestamp_strategy()) {
prop_assert!(is_timestamp(&ts));
}
}
proptest! {
#[test]
fn test_valid_numerics_are_recognized(num in numeric_strategy()) {
prop_assert!(is_numeric(&num));
}
}
proptest! {
#[test]
fn test_valid_identifiers_are_recognized(id in identifier_strategy()) {
if !id.is_empty() {
prop_assert!(is_identifier(&id));
}
}
}
proptest! {
#[test]
fn test_valid_person_names_are_recognized(name in person_name_strategy()) {
prop_assert!(is_person_name(&name));
}
}
proptest! {
#[test]
fn test_valid_emails_are_recognized(email in valid_email_strategy()) {
prop_assert!(is_email(&email));
}
}
proptest! {
#[test]
fn test_valid_phones_are_recognized(phone in valid_phone_strategy()) {
prop_assert!(is_phone_number(&phone));
}
}
proptest! {
#[test]
fn test_valid_ssns_are_recognized(ssn in valid_ssn_strategy()) {
prop_assert!(is_ssn(&ssn));
}
}
proptest! {
#[test]
fn test_invalid_dates_rejected(date in "[0-9]{8}") {
let month: u32 = date[4..6].parse().unwrap_or(0);
let day: u32 = date[6..8].parse().unwrap_or(0);
prop_assume!(month == 0 || month > 12 || day == 0 || day > 31);
prop_assert!(!is_date(&date));
}
}
proptest! {
#[test]
fn test_non_numeric_rejected(value in "[a-zA-Z]+") {
prop_assert!(!is_numeric(&value));
}
}
proptest! {
#[test]
fn test_control_chars_rejected_from_identifiers(value in any::<Vec<u8>>()) {
let s: String = value.iter()
.filter(|&&c| c >= 0x20 || c == 0x09) .map(|&c| c as char)
.collect();
let has_control = s.chars().any(|c| c.is_control() && c != '\t');
if has_control {
prop_assert!(!is_identifier(&s));
}
}
}
proptest! {
#[test]
fn test_numbers_in_names_rejected(name in "[a-zA-Z]*[0-9]+[a-zA-Z]*") {
prop_assert!(!is_person_name(&name));
}
}
proptest! {
#[test]
fn test_luhn_valid_numbers(luhn in valid_luhn_strategy()) {
prop_assert!(validate_luhn_checksum(&luhn));
}
}
proptest! {
#[test]
fn test_luhn_invalidated_by_digit_change(mut luhn in valid_luhn_strategy()) {
prop_assume!(!luhn.is_empty());
let idx = luhn.len() / 2;
let original_char = luhn.chars().nth(idx).unwrap_or('0');
let new_char = if original_char == '9' { '0' } else {
char::from_digit(original_char.to_digit(10).unwrap_or(0) + 1, 10).unwrap_or('0')
};
luhn.replace_range(idx..idx+1, &new_char.to_string());
let _ = validate_luhn_checksum(&luhn);
}
}
proptest! {
#[test]
fn test_within_range_inclusive(
value in 0i64..1000,
min in 0i64..500,
max in 500i64..1000
) {
let result = is_within_range(&value.to_string(), &min.to_string(), &max.to_string());
if value >= min && value <= max {
prop_assert!(result, "Value {} should be in range [{}, {}]", value, min, max);
} else {
prop_assert!(!result, "Value {} should NOT be in range [{}, {}]", value, min, max);
}
}
}
proptest! {
#[test]
fn test_within_range_decimals(
value in -1000.0f64..1000.0,
min in -500.0f64..0.0,
max in 0.0f64..500.0
) {
let result = is_within_range(&value.to_string(), &min.to_string(), &max.to_string());
if value >= min && value <= max {
prop_assert!(result);
} else {
prop_assert!(!result);
}
}
}
proptest! {
#[test]
fn test_math_relationship_gt(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "gt");
prop_assert_eq!(result, a > b);
}
}
proptest! {
#[test]
fn test_math_relationship_lt(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "lt");
prop_assert_eq!(result, a < b);
}
}
proptest! {
#[test]
fn test_math_relationship_eq(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "eq");
prop_assert_eq!(result, a == b);
}
}
proptest! {
#[test]
fn test_math_relationship_ge(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "ge");
prop_assert_eq!(result, a >= b);
}
}
proptest! {
#[test]
fn test_math_relationship_le(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "le");
prop_assert_eq!(result, a <= b);
}
}
proptest! {
#[test]
fn test_math_relationship_ne(a in any::<i64>(), b in any::<i64>()) {
let result = validate_mathematical_relationship(&a.to_string(), &b.to_string(), "ne");
prop_assert_eq!(result, a != b);
}
}
proptest! {
#[test]
fn test_birth_date_not_in_future(
year in 1900u32..=2025,
month in 1u32..=12,
day in 1u32..=28 ) {
let date = format!("{:04}{:02}{:02}", year, month, day);
prop_assert!(is_valid_birth_date(&date));
}
}
proptest! {
#[test]
fn test_issue_creation_never_panics(
code in ".*",
path in proptest::option::of(".*"),
detail in ".*"
) {
let issue = Issue::new(&code, Severity::Error, path.clone(), detail.clone());
prop_assert_eq!(issue.code, code);
prop_assert_eq!(issue.path, path);
prop_assert_eq!(issue.detail, detail);
}
}
proptest! {
#[test]
fn test_issue_error_creation(code in ".*", path in proptest::option::of(".*"), detail in ".*") {
let issue = Issue::error(&code, path.clone(), detail.clone());
prop_assert_eq!(issue.severity, Severity::Error);
}
}
proptest! {
#[test]
fn test_issue_warning_creation(code in ".*", path in proptest::option::of(".*"), detail in ".*") {
let issue = Issue::warning(&code, path.clone(), detail.clone());
prop_assert_eq!(issue.severity, Severity::Warning);
}
}
proptest! {
#[test]
fn test_coded_value_ascii_printable(value in "[ -~]*") {
prop_assert!(is_coded_value(&value));
}
}
proptest! {
#[test]
fn test_coded_value_with_unicode(value in ".*") {
let _ = is_coded_value(&value);
}
}
proptest! {
#[test]
fn test_sequence_id_positive(n in 1u32..=1000000) {
prop_assert!(is_sequence_id(&n.to_string()));
}
}
#[test]
fn test_sequence_id_zero_rejected() {
assert!(!is_sequence_id("0"));
}
proptest! {
#[test]
fn test_sequence_id_negative_rejected(n in any::<i32>().prop_filter("Negative", |&n| n < 0)) {
prop_assert!(!is_sequence_id(&n.to_string()));
}
}
proptest! {
#[test]
fn test_data_type_st_always_passes(value in ".*") {
prop_assert!(validate_data_type(&value, "ST"));
}
}
proptest! {
#[test]
fn test_data_type_tx_always_passes(value in ".*") {
prop_assert!(validate_data_type(&value, "TX"));
}
}
proptest! {
#[test]
fn test_data_type_ft_always_passes(value in ".*") {
prop_assert!(validate_data_type(&value, "FT"));
}
}
proptest! {
#[test]
fn test_data_type_unknown_always_passes(value in ".*", datatype in "[A-Z]{4,10}") {
prop_assert!(validate_data_type(&value, &datatype));
}
}
proptest! {
#[test]
fn test_date_consistency(date in valid_date_strategy()) {
prop_assert!(is_date(&date));
prop_assert!(is_timestamp(&date));
}
}
proptest! {
#[test]
fn test_timestamp_contains_date(ts in valid_timestamp_strategy()) {
if ts.len() >= 8 {
let date_part = &ts[0..8];
prop_assert!(is_date(date_part));
}
}
}
#[test]
fn test_empty_string_handling() {
assert!(is_string(""));
assert!(!is_date(""));
assert!(!is_time(""));
assert!(!is_numeric(""));
assert!(!is_sequence_id(""));
}
proptest! {
#[test]
fn test_whitespace_handling(ws in "\\s+") {
prop_assert!(!is_date(&ws));
prop_assert!(!is_time(&ws));
prop_assert!(!is_numeric(&ws));
}
}
proptest! {
#[test]
fn test_very_long_strings(n in 1usize..=10000) {
let long_string = "x".repeat(n);
let _ = is_date(&long_string);
let _ = is_time(&long_string);
let _ = is_identifier(&long_string);
let _ = validate_data_type(&long_string, "ST");
}
}
#[test]
fn test_is_timestamp_unicode_does_not_panic() {
let _ = is_timestamp("2023\u{1F525}\u{1F525}");
let _ = is_timestamp("\u{00E9}\u{00E9}\u{00E9}\u{00E9}\u{00E9}");
assert!(!is_timestamp("20230101\u{00E9}"));
}
fn is_string(_value: &str) -> bool {
true }