use core_policy::{PathPattern, PolicyError, MAX_RESOURCE_PATTERN_LENGTH};
#[test]
fn test_basic_exact_match() {
let p = PathPattern::new("/home/user/file.txt").unwrap();
assert!(p.matches("/home/user/file.txt"));
assert!(!p.matches("/home/user/other.txt"));
}
#[test]
fn test_single_wildcard() {
let p = PathPattern::new("/home/*/file.txt").unwrap();
assert!(p.matches("/home/user/file.txt"));
assert!(p.matches("/home/admin/file.txt"));
assert!(!p.matches("/home/user/other.txt"));
}
#[test]
fn test_question_mark() {
let p = PathPattern::new("/file?.txt").unwrap();
assert!(p.matches("/file1.txt"));
assert!(p.matches("/fileA.txt"));
assert!(!p.matches("/file12.txt"));
assert!(!p.matches("/file.txt"));
}
#[test]
fn test_star_doesnt_match_slash() {
let p = PathPattern::new("/home/*/file.txt").unwrap();
assert!(p.matches("/home/user/file.txt"));
assert!(!p.matches("/home/user/docs/file.txt"));
}
#[test]
fn test_double_star_treated_as_single() {
let p = PathPattern::new("/home/**/file.txt").unwrap();
assert!(p.matches("/home/user/file.txt"));
assert!(!p.matches("/home/user/docs/file.txt"));
}
#[test]
fn test_trailing_star() {
let p = PathPattern::new("/home/user/*").unwrap();
assert!(p.matches("/home/user/file.txt"));
assert!(p.matches("/home/user/document.pdf"));
}
#[test]
fn test_empty_pattern_and_path() {
let p = PathPattern::new("").unwrap();
assert!(p.matches(""));
assert!(!p.matches("/"));
}
#[test]
fn test_very_deep_path_100_levels() {
let pattern_parts: Vec<&str> = (0..100).map(|_| "*").collect();
let pattern = format!("/{}", pattern_parts.join("/"));
let path_parts: Vec<String> = (0..100).map(|i| i.to_string()).collect();
let path = format!("/{}", path_parts.join("/"));
let p = PathPattern::new(&pattern).unwrap();
assert!(p.matches(&path), "Should match 100-level deep path");
}
#[test]
fn test_very_deep_path_50_levels() {
let pattern_parts: Vec<&str> = (0..50).map(|_| "*").collect();
let pattern = format!("/{}", pattern_parts.join("/"));
let path_parts: Vec<String> = (0..50).map(|i| format!("d{}", i)).collect();
let path = format!("/{}", path_parts.join("/"));
let p = PathPattern::new(&pattern).unwrap();
assert!(
p.matches(&path),
"Should match 50-level deep path without stack overflow"
);
}
#[test]
fn test_extreme_depth_within_limits() {
let depth = 127;
let pattern_parts: Vec<&str> = (0..depth).map(|_| "*").collect();
let pattern = format!("/{}", pattern_parts.join("/"));
let path_parts: Vec<String> = (0..depth).map(|i| format!("s{}", i)).collect();
let path = format!("/{}", path_parts.join("/"));
assert!(
pattern.len() <= MAX_RESOURCE_PATTERN_LENGTH,
"Pattern should be within MAX_RESOURCE_PATTERN_LENGTH"
);
let p = PathPattern::new(&pattern).unwrap();
assert!(
p.matches(&path),
"Should match maximum-depth path without stack overflow (iterative implementation)"
);
}
#[test]
fn test_deep_path_with_mixed_wildcards() {
let p = PathPattern::new("/a/*/b/*/c/*/d/*/e/*/f/*/g/*/h/*").unwrap();
assert!(p.matches("/a/1/b/2/c/3/d/4/e/5/f/6/g/7/h/8"));
assert!(!p.matches("/a/1/b/2/c/3/d/4/e/5/f/6/g/7/h/8/i"));
assert!(!p.matches("/a/1/b/2/c/3/d/4/e/5/f/6/g/7"));
}
#[test]
fn test_many_consecutive_wildcards() {
let p = PathPattern::new("/*/*/*/*/*/*/*/*/*/*/*").unwrap();
assert!(p.matches("/a/b/c/d/e/f/g/h/i/j/k"));
assert!(!p.matches("/a/b/c/d/e/f/g/h/i/j"));
}
#[test]
fn test_star_at_end_matches_multiple_chars() {
let p = PathPattern::new("/home/user/file*").unwrap();
assert!(p.matches("/home/user/file"));
assert!(p.matches("/home/user/file.txt"));
assert!(p.matches("/home/user/file123.pdf"));
assert!(!p.matches("/home/user/other"));
}
#[test]
fn test_multiple_stars_in_segment() {
let p = PathPattern::new("/home/*/file*.txt").unwrap();
assert!(p.matches("/home/user/file1.txt"));
assert!(p.matches("/home/admin/fileABC.txt"));
assert!(!p.matches("/home/user/other.txt"));
}
#[test]
fn test_backtracking_with_star() {
let p = PathPattern::new("/a*/b").unwrap();
assert!(p.matches("/ab/b"));
assert!(p.matches("/aaa/b"));
assert!(p.matches("/axyz/b"));
assert!(!p.matches("/a/b/b"));
}
#[test]
fn test_complex_backtracking() {
let p = PathPattern::new("/*a*b*c*").unwrap();
assert!(p.matches("/abc"));
assert!(p.matches("/xaybzc"));
assert!(p.matches("/aaabbbccc"));
assert!(!p.matches("/ab"));
assert!(!p.matches("/ac"));
}
#[test]
fn test_pattern_longer_than_path() {
let p = PathPattern::new("/a/b/c/d/e").unwrap();
assert!(!p.matches("/a/b/c"));
}
#[test]
fn test_path_longer_than_pattern() {
let p = PathPattern::new("/a/b/c").unwrap();
assert!(!p.matches("/a/b/c/d/e"));
}
#[test]
fn test_only_wildcards() {
let p = PathPattern::new("*").unwrap();
assert!(p.matches("anything"));
assert!(p.matches("file.txt"));
assert!(!p.matches(""));
}
#[test]
fn test_question_marks_only() {
let p = PathPattern::new("???").unwrap();
assert!(p.matches("abc"));
assert!(p.matches("123"));
assert!(!p.matches("ab"));
assert!(!p.matches("abcd"));
}
#[test]
fn test_utf8_paths() {
let p = PathPattern::new("/home/*/файл.txt").unwrap();
assert!(p.matches("/home/user/файл.txt"));
assert!(!p.matches("/home/user/file.txt"));
}
#[test]
fn test_special_chars_in_path() {
let p = PathPattern::new("/home/user/*.txt").unwrap();
assert!(p.matches("/home/user/file-name.txt"));
assert!(p.matches("/home/user/file_name.txt"));
assert!(p.matches("/home/user/file.name.txt"));
}
#[test]
fn test_max_pattern_length_enforcement() {
let long_pattern = "a".repeat(MAX_RESOURCE_PATTERN_LENGTH + 1);
let result = PathPattern::new(long_pattern);
assert!(result.is_err());
if let Err(PolicyError::PatternTooLong { max, length }) = result {
assert_eq!(max, MAX_RESOURCE_PATTERN_LENGTH);
assert_eq!(length, MAX_RESOURCE_PATTERN_LENGTH + 1);
} else {
panic!("Expected PatternTooLong error");
}
}
#[test]
fn test_edge_case_star_before_slash() {
let p = PathPattern::new("*/file.txt").unwrap();
assert!(p.matches("dir/file.txt"));
assert!(!p.matches("dir/subdir/file.txt"));
}
#[test]
fn test_edge_case_multiple_slashes() {
let p = PathPattern::new("/home//user").unwrap();
assert!(p.matches("/home//user"));
assert!(!p.matches("/home/user"));
}