use super::parse_graceful;
use crate::uri::Uri;
#[cfg(feature = "idna")]
mod with_idna {
use super::*;
use crate::address::{Domain, Host};
#[test]
fn ascii_host_unchanged_zero_copy() {
let uri: Uri = parse_graceful("https://example.com/path").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "example.com");
assert_eq!(uri.to_string(), "https://example.com/path");
}
#[test]
fn german_idn_host_preserved_verbatim() {
let uri: Uri = parse_graceful("https://münchen.de/").unwrap();
let h = uri.host().unwrap();
assert_eq!(h.to_str(), "münchen.de");
assert_eq!(uri.to_string(), "https://münchen.de/");
}
#[test]
fn german_idn_converts_to_ace_on_demand() {
let uri: Uri = parse_graceful("https://münchen.de/").unwrap();
let owned = uri.host().unwrap().into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("non-ASCII host parses as Uninterpreted");
};
let d = Domain::try_from(uninterpreted).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[test]
fn japanese_idn_host_preserved_and_converts_on_demand() {
let uri: Uri = parse_graceful("https://日本.com/").unwrap();
let h = uri.host().unwrap();
assert_eq!(h.to_str(), "日本.com");
let owned = h.into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("expected Host::Uninterpreted");
};
let d = Domain::try_from(uninterpreted).unwrap();
assert!(d.as_str().starts_with("xn--"), "got {d}");
assert!(d.as_str().ends_with(".com"), "got {d}");
}
#[test]
fn already_ace_host_unchanged() {
let uri: Uri = parse_graceful("https://xn--mnchen-3ya.de/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "xn--mnchen-3ya.de");
let owned = uri.host().unwrap().into_owned();
assert!(matches!(owned, Host::Name(_)));
}
#[test]
fn uppercase_non_ascii_preserved_then_normalised_on_convert() {
let uri: Uri = parse_graceful("https://MÜNCHEN.de/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "MÜNCHEN.de");
let owned = uri.host().unwrap().into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("expected Host::Uninterpreted");
};
let d = Domain::try_from(uninterpreted).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[test]
fn mixed_labels_preserved_then_normalised_on_convert() {
let uri: Uri = parse_graceful("https://münchen.example.com/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "münchen.example.com");
let owned = uri.host().unwrap().into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("expected Host::Uninterpreted");
};
let d = Domain::try_from(uninterpreted).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.example.com");
}
#[test]
fn ipv4_address_unaffected() {
let uri: Uri = parse_graceful("https://1.2.3.4:8080/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "1.2.3.4");
}
#[test]
fn ipv6_address_unaffected() {
let uri: Uri = parse_graceful("https://[::1]:8080/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "::1");
}
#[test]
fn as_unicode_borrows_when_no_ace_labels() {
let d = Domain::try_from("example.com").unwrap();
let unicode = d.as_unicode();
assert!(matches!(unicode, crate::std::borrow::Cow::Borrowed(_)));
assert_eq!(&*unicode, "example.com");
}
#[test]
fn as_unicode_decodes_ace_labels() {
let d = Domain::try_from("xn--mnchen-3ya.de").unwrap();
let unicode = d.as_unicode();
assert!(matches!(unicode, crate::std::borrow::Cow::Owned(_)));
assert_eq!(&*unicode, "münchen.de");
}
#[test]
fn as_unicode_preserves_non_ascii_host_via_uri() {
let uri: Uri = parse_graceful("https://münchen.de/").unwrap();
let host = uri.host().unwrap();
assert_eq!(host.to_str(), "münchen.de");
assert_eq!(&*host.as_unicode(), "münchen.de");
}
#[test]
fn as_unicode_on_ip_returns_textual_form() {
let uri: Uri = parse_graceful("https://1.2.3.4/").unwrap();
assert_eq!(uri.host().unwrap().as_unicode(), "1.2.3.4");
let uri: Uri = parse_graceful("https://[::1]/").unwrap();
assert_eq!(uri.host().unwrap().as_unicode(), "::1");
}
#[test]
fn uts46_disallowed_codepoint_caught_on_domain_conversion() {
let uri: Uri = parse_graceful("https://a\u{fe6e}b.com/")
.expect("parser preserves the bytes; UTS #46 runs on convert");
let owned = uri.host().unwrap().into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("expected Host::Uninterpreted");
};
let r = Domain::try_from(uninterpreted);
assert!(r.is_err(), "UTS #46 must reject disallowed codepoint");
}
#[test]
fn domain_try_from_str_handles_idn() {
let d = Domain::try_from("münchen.de").unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[test]
fn domain_try_from_bytes_handles_idn() {
let d = Domain::try_from("münchen.de".as_bytes()).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[test]
fn domain_try_from_string_handles_idn() {
let d = Domain::try_from(String::from("münchen.de")).unwrap();
assert_eq!(d.as_str(), "xn--mnchen-3ya.de");
}
#[test]
fn strict_rejects_non_ascii_host() {
use crate::uri::ParseError;
for non_ascii in [
"https://münchen.de/",
"https://日本.com/",
"https://MÜNCHEN.de/",
"https://api.üñiçödé.example/",
] {
let r = crate::uri::Uri::parse_strict(non_ascii);
assert!(
matches!(r, Err(ParseError::StrictViolation)),
"strict must reject non-ASCII host {non_ascii:?}; got {r:?}"
);
}
}
#[test]
fn strict_accepts_already_ace_host() {
let u = crate::uri::Uri::parse_strict("https://xn--mnchen-3ya.de/").unwrap();
assert_eq!(u.host().unwrap().to_str(), "xn--mnchen-3ya.de");
}
#[test]
fn graceful_preserves_non_ascii_strict_rejects() {
let graceful = super::parse_graceful("https://münchen.de/").unwrap();
assert_eq!(graceful.host().unwrap().to_str(), "münchen.de");
let strict = crate::uri::Uri::parse_strict("https://münchen.de/");
strict.unwrap_err();
}
}
#[cfg(not(feature = "idna"))]
mod without_idna {
use super::*;
use crate::address::{Domain, Host};
#[test]
fn non_ascii_host_preserved_but_domain_conversion_errors() {
let uri: Uri = parse_graceful("https://münchen.de/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "münchen.de");
let owned = uri.host().unwrap().into_owned();
let Host::Uninterpreted(uninterpreted) = &owned else {
panic!("expected Host::Uninterpreted");
};
let err = Domain::try_from(uninterpreted).unwrap_err();
assert!(err.is_idna_not_enabled(), "got {err:?}");
}
#[test]
fn ascii_host_still_works() {
let uri: Uri = parse_graceful("https://example.com/").unwrap();
assert_eq!(uri.host().unwrap().to_str(), "example.com");
}
}