use pattern_core::Pattern;
#[test]
fn validate_all_valid() {
let pattern = Pattern::pattern(1, vec![Pattern::point(2), Pattern::point(3)]);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_ok());
let validated = result.unwrap();
assert_eq!(validated.value, 10);
assert_eq!(validated.elements[0].value, 20);
assert_eq!(validated.elements[1].value, 30);
}
#[test]
fn validate_one_invalid() {
let pattern = Pattern::pattern(1, vec![Pattern::point(-2), Pattern::point(3)]);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(errors[0], "value -2 is not positive");
}
#[test]
fn validate_multiple_invalid() {
let pattern = Pattern::pattern(
-1,
vec![
Pattern::point(2),
Pattern::point(-3),
Pattern::point(4),
Pattern::point(-5),
],
);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 3);
assert!(errors.contains(&"value -1 is not positive".to_string()));
assert!(errors.contains(&"value -3 is not positive".to_string()));
assert!(errors.contains(&"value -5 is not positive".to_string()));
}
#[test]
fn validate_error_ordering() {
let pattern = Pattern::pattern(
-1,
vec![Pattern::point(2), Pattern::point(-3), Pattern::point(4)],
);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 2);
assert_eq!(errors[0], "value -1 is not positive");
assert_eq!(errors[1], "value -3 is not positive");
}
#[test]
fn validate_no_short_circuit() {
use pattern_core::test_utils::helpers::EffectCounter;
let pattern = Pattern::pattern(
-1,
vec![Pattern::point(-2), Pattern::point(-3), Pattern::point(-4)],
);
let counter = EffectCounter::new();
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
counter.increment();
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 4);
assert_eq!(counter.count(), 4);
}
#[test]
fn validate_vs_traverse_result_short_circuit() {
use pattern_core::test_utils::helpers::EffectCounter;
let pattern = Pattern::pattern(
-1,
vec![Pattern::point(-2), Pattern::point(-3), Pattern::point(-4)],
);
let traverse_counter = EffectCounter::new();
let traverse_result: Result<Pattern<i32>, String> = pattern.traverse_result(|v| {
traverse_counter.increment();
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(traverse_result.is_err());
assert_eq!(traverse_counter.count(), 1);
let validate_counter = EffectCounter::new();
let validate_result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
validate_counter.increment();
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(validate_result.is_err());
assert_eq!(validate_counter.count(), 4);
}
#[test]
fn validate_nested_multiple_errors() {
let pattern = Pattern::pattern(
-1,
vec![
Pattern::pattern(2, vec![Pattern::point(-3), Pattern::point(4)]),
Pattern::point(-5),
],
);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 3);
assert_eq!(errors[0], "value -1 is not positive");
assert_eq!(errors[1], "value -3 is not positive");
assert_eq!(errors[2], "value -5 is not positive");
}
#[test]
fn validate_type_transformation() {
let pattern = Pattern::pattern(
"1",
vec![
Pattern::point("2"),
Pattern::point("invalid"),
Pattern::point("3"),
Pattern::point("bad"),
],
);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|s| {
s.parse::<i32>()
.map_err(|e| format!("parse error for '{}': {}", s, e))
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 2);
assert!(errors[0].contains("invalid"));
assert!(errors[1].contains("bad"));
}
#[test]
fn validate_atomic_valid() {
let pattern = Pattern::point(5);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_ok());
assert_eq!(result.unwrap().value, 50);
}
#[test]
fn validate_atomic_invalid() {
let pattern = Pattern::point(-5);
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(|v| {
if *v > 0 {
Ok(*v * 10)
} else {
Err(format!("value {} is not positive", v))
}
});
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(errors[0], "value -5 is not positive");
}
#[test]
fn validate_all_single_call_per_value() {
use pattern_core::test_utils::helpers::EffectCounter;
use std::sync::Arc;
let pattern = Pattern::pattern(
1,
vec![Pattern::point(2), Pattern::point(3), Pattern::point(4)],
);
let counter = Arc::new(EffectCounter::new());
let counter_clone = counter.clone();
let result: Result<Pattern<i32>, Vec<String>> = pattern.validate_all(move |v| {
counter_clone.increment();
Ok(*v * 10)
});
assert!(result.is_ok());
assert_eq!(counter.count(), 4);
}