use guardflow::validators::{
MatchesRegex, MaxLength, MinLength, NoPii, NotEmpty, OneOf, ValidJson,
};
use guardflow::{Guard, guard_with_retry};
#[test]
fn guard_collects_every_failure_not_just_the_first() {
let guard = Guard::new().with(NotEmpty).with(MaxLength(5));
let result = guard.check("this is way too long");
assert!(!result.passed);
assert_eq!(result.failures.len(), 1);
assert_eq!(result.failures[0].validator, "max_length");
}
#[test]
fn guard_passes_when_every_validator_passes() {
let guard = Guard::new().with(NotEmpty).with(MaxLength(20));
let result = guard.check("short and valid");
assert!(result.passed);
assert!(result.failures.is_empty());
}
#[test]
fn not_empty_fails_on_whitespace_only() {
let guard = Guard::new().with(NotEmpty);
assert!(!guard.check(" ").passed);
}
#[test]
fn min_length_fails_below_threshold() {
let guard = Guard::new().with(MinLength(10));
assert!(!guard.check("short").passed);
assert!(guard.check("this is long enough").passed);
}
#[test]
fn matches_regex_validates_shape() {
let guard = Guard::new().with(MatchesRegex::new(r"^\d{3}-\d{4}$").unwrap());
assert!(guard.check("123-4567").passed);
assert!(!guard.check("not a match").passed);
}
#[test]
fn one_of_requires_exact_membership() {
let guard = Guard::new().with(OneOf::new(["yes", "no", "maybe"]));
assert!(guard.check("yes").passed);
assert!(!guard.check("Yes").passed);
}
#[test]
fn valid_json_rejects_malformed_input() {
let guard = Guard::new().with(ValidJson);
assert!(guard.check(r#"{"a": 1}"#).passed);
assert!(!guard.check("{not json").passed);
}
#[test]
fn no_pii_flags_email_addresses() {
let guard = Guard::new().with(NoPii::default());
assert!(guard.check("no personal data here").passed);
assert!(!guard.check("contact me at alice@example.com").passed);
}
#[tokio::test]
async fn guard_with_retry_stops_at_first_passing_attempt() {
let guard = Guard::new().with(MinLength(5));
let mut attempts = 0;
let result = guard_with_retry(&guard, 5, |_| {
attempts += 1;
let attempt = attempts;
async move {
if attempt < 3 {
"no".to_string()
} else {
"long enough".to_string()
}
}
})
.await;
assert_eq!(result, Ok("long enough".to_string()));
assert_eq!(attempts, 3);
}
#[tokio::test]
async fn guard_with_retry_returns_last_failure_after_exhausting_attempts() {
let guard = Guard::new().with(MinLength(100));
let result = guard_with_retry(&guard, 2, |_| async { "too short".to_string() }).await;
let Err(failure) = result else {
panic!("expected failure after exhausting attempts");
};
assert!(!failure.passed);
}