#![allow(clippy::regex_creation_in_loops)]
#[cfg(not(target_family = "wasm"))]
use regex::Regex;
#[cfg(not(target_family = "wasm"))]
use rudof_generate::field_generators::pattern::PatternGenerator;
#[cfg(not(target_family = "wasm"))]
use rudof_generate::field_generators::{FieldGenerator, GenerationContext};
#[cfg(not(target_family = "wasm"))]
use serde_json::json;
#[cfg(not(target_family = "wasm"))]
use std::collections::HashSet;
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_with_length_constraints() {
let generator = PatternGenerator;
let mut context = GenerationContext::new(
"http://example.org/constrainedString".to_string(),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context
.parameters
.insert("pattern".to_string(), json!("[A-Z]{3}\\d{3}"));
for _ in 0..10 {
let result = generator.generate(&context).unwrap();
println!("Generated constrained string: {result}");
let pattern_regex = Regex::new(r"^[A-Z]{3}\d{3}$").unwrap();
assert!(
pattern_regex.is_match(&result),
"Should match license plate pattern: {result}"
);
assert_eq!(result.len(), 6, "Should be exactly 6 characters: {result}");
let letters = &result[0..3];
assert!(
letters.chars().all(|c| c.is_ascii_uppercase()),
"First 3 should be uppercase: {letters}"
);
let digits = &result[3..6];
assert!(
digits.chars().all(|c| c.is_ascii_digit()),
"Last 3 should be digits: {digits}"
);
}
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_with_enumeration() {
let generator = PatternGenerator;
let mut context = GenerationContext::new(
"http://example.org/numericCode".to_string(),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!("\\d{3}"));
let mut generated_values = HashSet::new();
for _ in 0..10 {
let result = generator.generate(&context).unwrap();
generated_values.insert(result.clone());
println!("Generated numeric code: {result}");
assert_eq!(result.len(), 3, "Should be 3 digits: {result}");
assert!(
result.chars().all(|c| c.is_ascii_digit()),
"Should be all digits: {result}"
);
let num: u32 = result.parse().unwrap();
assert!(num <= 999, "Should be a 3-digit pattern (000-999): {num}");
}
assert!(generated_values.len() > 1, "Should generate variety from enumeration");
println!("Generated enumerated values: {generated_values:?}");
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_with_different_datatypes() {
let generator = PatternGenerator;
let mut token_context = GenerationContext::new(
"http://example.org/token".to_string(),
"http://www.w3.org/2001/XMLSchema#token".to_string(),
"test_subject".to_string(),
);
token_context
.parameters
.insert("pattern".to_string(), json!("[A-Za-z0-9_-]+"));
let token_result = generator.generate(&token_context).unwrap();
println!("Generated token: {token_result}");
let token_regex = Regex::new(r"^[A-Za-z0-9_-]+$").unwrap();
assert!(
token_regex.is_match(&token_result),
"Token should match pattern: {token_result}"
);
let mut custom_context = GenerationContext::new(
"http://example.org/custom".to_string(),
"http://example.org/CustomType".to_string(),
"test_subject".to_string(),
);
custom_context
.parameters
.insert("pattern".to_string(), json!("CUSTOM-\\d{4}"));
let custom_result = generator.generate(&custom_context).unwrap();
println!("Generated custom type: {custom_result}");
let custom_regex = Regex::new(r"^CUSTOM-\d{4}$").unwrap();
assert!(
custom_regex.is_match(&custom_result),
"Custom should match pattern: {custom_result}"
);
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_shacl_constraint_integration() {
let generator = PatternGenerator;
let shacl_patterns = vec![
("productCode", "PROD-\\d{4}-[A-Z]{2}"),
("serialNumber", "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}"),
("category", "[A-Z]{3,8}"),
];
let mut generated_results = Vec::new();
for (prop_name, pattern) in shacl_patterns {
let mut context = GenerationContext::new(
format!("http://example.org/{prop_name}"),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context).unwrap();
println!("Generated SHACL {prop_name} pattern: {result}");
generated_results.push((prop_name, result));
}
assert_eq!(generated_results.len(), 3, "Should generate all 3 SHACL pattern types");
for (prop_name, result) in &generated_results {
match *prop_name {
"productCode" => {
assert!(
result.starts_with("PROD-"),
"Product code should start with PROD-: {result}"
);
assert!(result.len() >= 10, "Product code should have proper length: {result}");
},
"serialNumber" => {
assert!(result.contains("-"), "Serial number should contain hyphens: {result}");
let parts: Vec<&str> = result.split('-').collect();
assert!(parts.len() >= 3, "Serial number should have multiple parts: {result}");
},
"category" => {
assert!(
result
.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_alphabetic()),
"Category should be letters: {result}"
);
assert!(
result.len() >= 3 && result.len() <= 8,
"Category should be 3-8 chars: {result}"
);
},
_ => {},
}
}
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_shex_cardinality_integration() {
let generator = PatternGenerator;
let phone_patterns = vec![
("primaryPhone", "\\d{3}-\\d{3}-\\d{4}"),
("alternatePhone", "\\d{3}-\\d{3}-\\d{4}"),
("emergencyContacts", "\\d{3}-\\d{3}-\\d{4}"),
];
let tag_patterns = vec![
("shortTag", "[A-Z]{2}"),
("mediumTag", "[A-Z]{3}"),
("longTag", "[A-Z]{5}"),
];
let mut all_results = Vec::new();
for (name, pattern) in phone_patterns {
let mut context = GenerationContext::new(
format!("http://example.org/{name}"),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context).unwrap();
all_results.push((name, result));
println!("Generated {}: {}", name, all_results.last().unwrap().1);
}
for (name, pattern) in tag_patterns {
let mut context = GenerationContext::new(
format!("http://example.org/{name}"),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context).unwrap();
all_results.push((name, result));
println!("Generated {}: {}", name, all_results.last().unwrap().1);
}
assert_eq!(all_results.len(), 6, "Should generate all 6 pattern variations");
for (name, result) in &all_results {
if name.contains("Phone") || name.contains("Contacts") {
assert!(result.contains("-"), "Phone numbers should contain hyphens: {result}");
assert_eq!(result.len(), 12, "Phone numbers should be 12 chars: {result}");
} else if name.contains("Tag") {
assert!(
result.chars().all(|c| c.is_ascii_uppercase()),
"Tags should be uppercase: {result}"
);
assert!(
result.len() >= 2 && result.len() <= 5,
"Tags should be 2-5 chars: {result}"
);
}
}
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_generator_robustness() {
let generator = PatternGenerator;
let test_cases = vec![
("", "empty pattern"),
("(", "unmatched parenthesis"),
("[", "unmatched bracket"),
("\\", "trailing backslash"),
("*", "invalid quantifier"),
("+", "invalid quantifier at start"),
("?", "invalid quantifier at start"),
("{5}", "invalid quantifier at start"),
("a{", "incomplete quantifier"),
("a{5", "incomplete quantifier"),
("a{5,", "incomplete quantifier"),
("[a-", "incomplete range"),
("(?:", "incomplete group"),
("\\x", "invalid escape"),
("\\u", "incomplete unicode"),
];
for (pattern, description) in test_cases {
println!("Testing {description}: '{pattern}'");
let mut context = GenerationContext::new(
"http://example.org/malformed".to_string(),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context);
match result {
Ok(value) => {
assert!(
!value.is_empty(),
"Should generate non-empty fallback for {description}: '{pattern}'"
);
println!(" -> Generated fallback: '{value}'");
},
Err(e) => {
println!(" -> Error (acceptable): {e}");
},
}
}
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_generator_complex_integration() {
let generator = PatternGenerator;
let complex_patterns = vec![
("organizationId", "ORG-\\d{6}-[A-Z]{2}"),
("employeeId", "EMP-\\d{5}-[A-Z]{1}"),
("departmentCode", "DEPT-[A-Z]{3,4}"),
("email", "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"),
("phone", "\\d{3}-\\d{3}-\\d{4}"),
];
let mut generated_results = Vec::new();
for (prop_name, pattern) in complex_patterns {
let mut context = GenerationContext::new(
format!("http://example.org/{prop_name}"),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context).unwrap();
generated_results.push((prop_name, result));
println!(
"Generated {} pattern: {}",
prop_name,
generated_results.last().unwrap().1
);
}
assert_eq!(
generated_results.len(),
5,
"Should generate all 5 complex pattern types"
);
for (prop_name, result) in &generated_results {
match *prop_name {
"organizationId" => {
assert!(
result.starts_with("ORG-"),
"Organization ID should start with ORG-: {result}"
);
assert!(
result.len() >= 11,
"Organization ID should have proper length: {result}"
);
},
"employeeId" => {
assert!(
result.starts_with("EMP-"),
"Employee ID should start with EMP-: {result}"
);
assert!(result.len() >= 9, "Employee ID should have proper length: {result}");
},
"email" => {
assert!(result.contains("@"), "Email should contain @: {result}");
assert!(result.contains("."), "Email should contain .: {result}");
},
"phone" => {
assert!(result.contains("-"), "Phone should contain -: {result}");
assert_eq!(result.len(), 12, "Phone should be exactly 12 chars: {result}");
},
_ => {},
}
}
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_generator_consistency() {
let generator = PatternGenerator;
let mut context = GenerationContext::new(
"http://example.org/consistent".to_string(),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
"test_subject".to_string(),
);
context
.parameters
.insert("pattern".to_string(), json!("[A-Z]{2}\\d{4}"));
let mut results = Vec::new();
for _ in 0..100 {
let result = generator.generate(&context).unwrap();
results.push(result);
}
let pattern_regex = Regex::new(r"^[A-Z]{2}\d{4}$").unwrap();
for result in &results {
assert!(
pattern_regex.is_match(result),
"All results should match pattern: {result}"
);
}
let unique_results: HashSet<_> = results.iter().collect();
let variety_ratio = unique_results.len() as f64 / results.len() as f64;
println!(
"Generated {} unique results out of {} total (variety: {:.2}%)",
unique_results.len(),
results.len(),
variety_ratio * 100.0
);
assert!(
variety_ratio > 0.5,
"Should generate reasonable variety: {:.2}%",
variety_ratio * 100.0
);
}
#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_pattern_generator_memory_efficiency() {
let generator = PatternGenerator;
let patterns = [
"\\d{3}-\\d{3}-\\d{4}",
"[A-Z]{2,4}\\d{3,5}",
"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
"https?://[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
"#[0-9A-Fa-f]{6}",
];
for (i, pattern) in patterns.iter().enumerate() {
for j in 0..100 {
let mut context = GenerationContext::new(
format!("http://example.org/prop{i}-{j}"),
"http://www.w3.org/2001/XMLSchema#string".to_string(),
format!("subject_{j}"),
);
context.parameters.insert("pattern".to_string(), json!(pattern));
let result = generator.generate(&context);
assert!(
result.is_ok(),
"Should generate successfully for pattern {i}: {pattern}"
);
let value = result.unwrap();
assert!(!value.is_empty(), "Should generate non-empty value");
}
}
println!(
"Successfully generated {} values across {} patterns",
100 * patterns.len(),
patterns.len()
);
}