use qubit_fs::Path;
use qubit_fs::path::PathComponent;
use qubit_fs::path::PathSemantics;
use qubit_fs::path::RelativePath;
#[test]
fn test_path_normalized_parse_and_safe_join() {
let root = Path::parse("/bucket").expect("absolute path should parse");
let child = PathComponent::parse("object").expect("component should parse");
let relative = RelativePath::parse("dir/file").expect("relative path should parse");
assert_eq!(root.child(&child).as_str(), "/bucket/object");
assert_eq!(root.join(&relative).as_str(), "/bucket/dir/file");
}
#[test]
fn test_path_literal_parse_preserves_repeated_separator_and_dot_text() {
let path = Path::parse_with_semantics("key//./value", PathSemantics::ObjectKey)
.expect("literal provider key should parse");
assert_eq!(path.as_str(), "key//./value");
}
#[test]
fn test_path_components_do_not_emit_a_root_placeholder() {
let root = Path::root();
assert!(root.is_absolute());
assert_eq!(root.components().collect::<Vec<_>>(), Vec::<&str>::new());
}
#[test]
fn test_path_from_components_constructs_absolute_root() {
let path = Path::from_components(true, Vec::<&str>::new())
.expect("an empty absolute component sequence should form the root");
assert_eq!(Path::root(), path);
}
#[test]
fn test_path_from_components_rejects_empty_relative_path() {
assert!(Path::from_components(false, Vec::<&str>::new()).is_err());
}
#[test]
fn test_path_from_components_preserves_encoded_separator_component() {
let path =
Path::from_components(true, ["bucket", "%2F"]).expect("encoded separator text should be a valid component");
assert_eq!("/bucket/%2F", path.as_str());
assert_eq!(vec!["bucket", "%2F"], path.components().collect::<Vec<_>>());
}
#[test]
fn test_path_from_components_rejects_invalid_components() {
for invalid in [".", "..", "nul\0byte"] {
assert!(
Path::from_components(false, [invalid]).is_err(),
"{invalid:?} must fail"
);
}
}
#[test]
fn test_path_components_preserve_literal_leading_separator_boundary() {
let path = Path::parse_literal("/bucket/key").expect("literal path should parse");
assert_eq!(path.components().collect::<Vec<_>>(), vec!["", "bucket", "key"]);
}
#[test]
fn test_path_normalizes_hierarchical_text_and_exposes_attributes() {
let path = Path::parse("/bucket//./folder/../object").expect("hierarchical path should normalize");
assert_eq!("/bucket/object", path.as_str());
assert_eq!("/bucket/object", path.to_string());
assert_eq!("/bucket/object", path.as_ref());
assert!(path.is_absolute());
assert_eq!(PathSemantics::Hierarchical, path.semantics());
assert_eq!(vec!["bucket", "object"], path.components().collect::<Vec<_>>());
}
#[test]
fn test_path_canonical_text_round_trips() {
let path = Path::parse("/bucket//./folder/../object").expect("hierarchical path should normalize");
let reparsed = Path::parse(path.as_str()).expect("canonical path should reparse");
assert_eq!(path, reparsed);
}
#[test]
fn test_path_provider_specific_semantics_preserves_literal_text() {
let path = Path::parse_with_semantics("provider//./key", PathSemantics::ProviderSpecific)
.expect("provider-specific path should parse");
assert_eq!("provider//./key", path.as_str());
assert_eq!(PathSemantics::ProviderSpecific, path.semantics());
assert!(!path.is_absolute());
}
#[test]
fn test_path_rejects_empty_nul_and_root_escape() {
for invalid in ["", "nul\0byte", "../outside", "/../../outside", "."] {
assert!(Path::parse(invalid).is_err(), "{invalid:?} must fail");
}
}
#[test]
fn test_path_file_name_handles_root_and_trailing_separator() {
assert_eq!(None, Path::root().file_name());
assert_eq!(
Some("object"),
Path::parse("/bucket/object")
.expect("hierarchical path should parse")
.file_name()
);
assert_eq!(
Some("object"),
Path::parse_literal("/bucket/object")
.expect("literal path should parse")
.file_name()
);
assert_eq!(
None,
Path::parse_literal("/bucket/object/")
.expect("literal path should parse")
.file_name()
);
}
#[test]
fn test_path_public_operations_are_callable_without_reparsing() {
let root = Path::root();
let component = PathComponent::parse("child").expect("component");
let relative = RelativePath::parse("nested").expect("relative path");
let child: fn(&Path, &PathComponent) -> Path = Path::child;
let join: fn(&Path, &RelativePath) -> Path = Path::join;
let as_ref: for<'a> fn(&'a Path) -> &'a str = Path::as_ref;
assert_eq!("/child", child(&root, &component).as_str());
assert_eq!("/nested", join(&root, &relative).as_str());
assert_eq!("/", as_ref(&root));
}