use super::parse_graceful;
use crate::uri::PathRef;
fn raw_segments(uri_str: &str) -> Vec<String> {
let u = parse_graceful(uri_str).unwrap();
u.path()
.unwrap()
.segments()
.map(|s| s.as_encoded_str().into_owned())
.collect()
}
fn decoded_segments(uri_str: &str) -> Vec<String> {
let u = parse_graceful(uri_str).unwrap();
u.path()
.unwrap()
.segments()
.map(|s| s.as_decoded_str().into_owned())
.collect()
}
#[test]
fn root_yields_one_empty_segment() {
assert_eq!(raw_segments("/"), vec![""]);
}
#[test]
fn single_segment() {
assert_eq!(raw_segments("/foo"), vec!["foo"]);
}
#[test]
fn multi_segment() {
assert_eq!(raw_segments("/foo/bar/baz"), vec!["foo", "bar", "baz"]);
}
#[test]
fn trailing_slash_yields_empty_segment() {
assert_eq!(raw_segments("/foo/"), vec!["foo", ""]);
}
#[test]
fn double_slash_preserves_empty_segment() {
assert_eq!(raw_segments("/a//b"), vec!["a", "", "b"]);
}
#[test]
fn deep_path() {
assert_eq!(raw_segments("/a/b/c/d/e"), vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn absolute_form_empty_path_yields_nothing() {
let u = parse_graceful("http://example.com").unwrap();
let segs: Vec<_> = u.path().unwrap().segments().collect();
assert!(segs.is_empty());
}
#[test]
fn opaque_path_splits_from_start() {
let u = parse_graceful("urn:isbn:0451450523").unwrap();
let segs: Vec<_> = u
.path()
.unwrap()
.segments()
.map(|s| s.as_encoded_str().into_owned())
.collect();
assert_eq!(segs, vec!["isbn:0451450523"]);
}
#[test]
fn opaque_path_without_slashes() {
let u = parse_graceful("mailto:user@example.com").unwrap();
let segs: Vec<_> = u
.path()
.unwrap()
.segments()
.map(|s| s.as_encoded_str().into_owned())
.collect();
assert_eq!(segs, vec!["user@example.com"]);
}
#[test]
fn opaque_path_with_slashes_splits_from_start() {
let u = parse_graceful("data:text/plain").unwrap();
let segs: Vec<_> = u
.path()
.unwrap()
.segments()
.map(|s| s.as_encoded_str().into_owned())
.collect();
assert_eq!(segs, vec!["text", "plain"]);
}
#[test]
fn asterisk_has_no_path_so_no_segments() {
let u = parse_graceful("*").unwrap();
assert!(u.path().is_none());
}
#[test]
fn absolute_form_segments() {
assert_eq!(
raw_segments("https://api.example.com/v1/users/42"),
vec!["v1", "users", "42"]
);
}
#[test]
fn absolute_form_with_trailing_slash() {
assert_eq!(
raw_segments("https://api.example.com/v1/users/"),
vec!["v1", "users", ""]
);
}
#[test]
fn decoded_simple_space() {
assert_eq!(decoded_segments("/hello%20world"), vec!["hello world"]);
}
#[test]
fn decoded_slash_in_segment() {
assert_eq!(
raw_segments("/admin/%2F../secret"),
vec!["admin", "%2F..", "secret"]
);
assert_eq!(
decoded_segments("/admin/%2F../secret"),
vec!["admin", "/..", "secret"]
);
}
#[test]
fn decoded_utf8_segment() {
assert_eq!(decoded_segments("/caf%C3%A9"), vec!["café"]);
}
#[test]
fn decoded_no_percent_borrows() {
let u = parse_graceful("/foo/bar").unwrap();
let segs: Vec<_> = u.path().unwrap().segments().collect();
assert!(matches!(
segs[0].as_decoded_str(),
crate::std::borrow::Cow::Borrowed(_)
));
assert!(matches!(
segs[1].as_decoded_str(),
crate::std::borrow::Cow::Borrowed(_)
));
}
#[test]
fn decoded_with_percent_owns() {
let u = parse_graceful("/hello%20world").unwrap();
let seg = u.path().unwrap().segments().next().unwrap();
assert!(matches!(
seg.as_decoded_str(),
crate::std::borrow::Cow::Owned(_)
));
}
#[test]
fn decoded_invalid_utf8_uses_replacement_char() {
let u = parse_graceful("/%FF").unwrap();
let seg = u.path().unwrap().segments().next().unwrap();
let decoded = seg.as_decoded_str();
assert!(decoded.contains('\u{FFFD}'), "got {decoded:?}");
}
#[test]
fn encoded_path_and_segment_preserve_valid_pct_only() {
let path = PathRef::from_raw_str("/hello world/%2F/%zz/%");
assert_eq!(path.as_encoded_str(), "/hello%20world/%2F/%25zz/%25");
let segments: Vec<_> = path
.segments()
.map(|segment| segment.as_encoded_str().into_owned())
.collect();
assert_eq!(segments, vec!["hello%20world", "%2F", "%25zz", "%25"],);
}
#[test]
fn trimmed_slashes_is_typed_and_preserves_inner_encoding() {
let path = PathRef::from_raw_str("///hello world/%2F///");
let trimmed = path.trimmed_slashes();
assert_eq!(trimmed.as_encoded_str(), "hello%20world/%2F");
assert_eq!(
trimmed
.segments()
.map(|segment| segment.as_encoded_str().into_owned())
.collect::<Vec<_>>(),
vec!["hello%20world", "%2F"],
);
}
#[test]
fn segment_range_borrows_rooted_segment_windows() {
let path = PathRef::from_raw_str("/api/users/42/orders");
assert_eq!(
path.segment_range(0, 2).unwrap().as_encoded_str(),
"/api/users"
);
assert_eq!(
path.segment_range(1, 2).unwrap().as_encoded_str(),
"/users/42"
);
assert_eq!(path.segment_range(2, 1).unwrap().as_encoded_str(), "/42");
assert_eq!(path.segment_range(3, 2), None);
assert_eq!(path.segment_range(1, 0), None);
}
#[test]
fn segment_range_preserves_empty_segments() {
let path = PathRef::from_raw_str("/a//b/");
assert_eq!(path.segment_range(1, 1).unwrap().as_encoded_str(), "/");
assert_eq!(path.segment_range(1, 2).unwrap().as_encoded_str(), "//b");
assert_eq!(path.segment_range(3, 1).unwrap().as_encoded_str(), "/");
}
#[test]
fn empty_segment_is_empty() {
let u = parse_graceful("/foo/").unwrap();
let segs: Vec<_> = u.path().unwrap().segments().collect();
assert!(!segs[0].is_empty()); assert!(segs[1].is_empty()); }
#[test]
fn iterator_fused_after_exhaustion() {
let u = parse_graceful("/foo").unwrap();
let mut it = u.path().unwrap().segments();
assert_eq!(it.next().unwrap().as_encoded_str(), "foo");
assert!(it.next().is_none());
assert!(it.next().is_none());
assert!(it.next().is_none());
}
#[test]
fn iterator_count_matches() {
for (path, expected_count) in [
("/foo", 1),
("/foo/bar", 2),
("/foo/bar/baz", 3),
("/", 1),
("/foo/", 2),
("/a//b", 3),
] {
let u = parse_graceful(path).unwrap();
assert_eq!(
u.path().unwrap().segments().count(),
expected_count,
"count mismatch for {path:?}"
);
}
}
#[test]
fn exact_size_len_and_size_hint() {
for (path, n) in [
("/foo", 1usize),
("/foo/bar", 2),
("/foo/bar/baz", 3),
("/", 1),
("/foo/", 2),
("/a//b", 3),
] {
let u = parse_graceful(path).unwrap();
let it = u.path().unwrap().segments();
assert_eq!(it.len(), n, "len for {path:?}");
assert_eq!(it.size_hint(), (n, Some(n)), "size_hint for {path:?}");
}
let u = parse_graceful("http://example.com").unwrap();
assert_eq!(u.path().unwrap().segments().len(), 0);
}
#[test]
fn len_tracks_remaining_as_it_advances() {
let u = parse_graceful("/a/b/c").unwrap();
let mut it = u.path().unwrap().segments();
assert_eq!(it.len(), 3);
it.next();
assert_eq!(it.len(), 2);
it.next();
assert_eq!(it.len(), 1);
it.next();
assert_eq!(it.len(), 0);
assert!(it.next().is_none());
assert_eq!(it.len(), 0);
}
#[test]
fn positional_accessors() {
let u = parse_graceful("/v1/users/42").unwrap();
let p = u.path().unwrap();
assert_eq!(p.first_segment().unwrap().as_encoded_str(), "v1");
assert_eq!(p.nth_segment(1).unwrap().as_encoded_str(), "users");
assert_eq!(p.last_segment().unwrap().as_encoded_str(), "42");
assert_eq!(p.nth_segment(3), None);
assert_eq!(p.segment_count(), 3);
}
#[test]
fn last_segment_of_trailing_slash_is_empty() {
let u = parse_graceful("/foo/").unwrap();
let p = u.path().unwrap();
assert!(p.last_segment().unwrap().is_empty());
assert_eq!(p.segment_count(), 2);
}
#[test]
fn accessors_on_empty_path() {
let u = parse_graceful("http://example.com").unwrap();
let p = u.path().unwrap();
assert_eq!(p.first_segment(), None);
assert_eq!(p.last_segment(), None);
assert_eq!(p.nth_segment(0), None);
assert_eq!(p.segment_count(), 0);
}