use sxurl::*;
#[test]
fn test_url_normalization_basic() {
let test_cases = vec![
("HTTPS://EXAMPLE.COM/", "https://example.com/"),
("HTTP://DOCS.RS/PATH", "http://docs.rs/PATH"),
("https://Example.Org/Test", "https://example.org/Test"),
("FTP://FILES.EXAMPLE.COM/", "ftp://files.example.com/"),
];
for (input, expected) in test_cases {
let result = normalize_url(input).unwrap();
assert_eq!(result.as_str(), expected, "Normalization failed for: {}", input);
}
}
#[test]
fn test_url_normalization_idna() {
let test_cases = vec![
"https://café.com/",
"https://москва.рф/",
"https://日本.jp/",
];
for url in test_cases {
let result = normalize_url(url);
assert!(result.is_ok(), "IDNA normalization should work for: {}", url);
let normalized = result.unwrap();
assert!(normalized.host_str().unwrap().is_ascii(), "Host should be ASCII after IDNA: {}", url);
}
}
#[test]
fn test_scheme_validation() {
let valid_schemes = vec![
"https://example.com/",
"http://example.com/",
"ftp://example.com/",
];
for url in valid_schemes {
let result = normalize_url(url);
assert!(result.is_ok(), "Should accept valid scheme: {}", url);
}
let invalid_schemes = vec![
"ws://example.com/",
"file:///path",
"mailto:test@example.com",
"data:text/plain,hello",
"javascript:alert('test')",
];
for url in invalid_schemes {
let result = normalize_url(url);
assert!(result.is_err(), "Should reject invalid scheme: {}", url);
}
}
#[test]
fn test_component_extraction() {
let test_cases = vec![
(
"https://docs.rs/",
UrlComponents {
scheme: "https".to_string(),
tld: "rs".to_string(),
domain: "docs".to_string(),
subdomain: "".to_string(),
port: 443,
path: "/".to_string(),
query: "".to_string(),
fragment: "".to_string(),
}
),
(
"http://api.example.com:8080/search?q=test#results",
UrlComponents {
scheme: "http".to_string(),
tld: "com".to_string(),
domain: "example".to_string(),
subdomain: "api".to_string(),
port: 8080,
path: "/search".to_string(),
query: "q=test".to_string(),
fragment: "results".to_string(),
}
),
];
for (url, expected) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.scheme, expected.scheme, "Scheme mismatch for: {}", url);
assert_eq!(components.tld, expected.tld, "TLD mismatch for: {}", url);
assert_eq!(components.domain, expected.domain, "Domain mismatch for: {}", url);
assert_eq!(components.subdomain, expected.subdomain, "Subdomain mismatch for: {}", url);
assert_eq!(components.port, expected.port, "Port mismatch for: {}", url);
assert_eq!(components.path, expected.path, "Path mismatch for: {}", url);
assert_eq!(components.query, expected.query, "Query mismatch for: {}", url);
assert_eq!(components.fragment, expected.fragment, "Fragment mismatch for: {}", url);
}
}
#[test]
fn test_default_ports() {
let test_cases = vec![
("https://example.com/", 443),
("http://example.com/", 80),
("ftp://example.com/", 21),
("https://example.com:443/", 443), ("https://example.com:8443/", 8443), ];
for (url, expected_port) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.port, expected_port, "Port mismatch for: {}", url);
}
}
#[test]
fn test_path_preservation() {
let test_cases = vec![
("https://example.com/path", "/path"),
("https://example.com/path/", "/path/"),
("https://example.com/path%20with%20spaces", "/path%20with%20spaces"),
("https://example.com/path?query", "/path"),
("https://example.com/", "/"),
("https://example.com", "/"),
];
for (url, expected_path) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.path, expected_path, "Path mismatch for: {}", url);
}
}
#[test]
fn test_query_preservation() {
let test_cases = vec![
("https://example.com/?q=test", "q=test"),
("https://example.com/?q=test&lang=en", "q=test&lang=en"),
("https://example.com/?q=hello%20world", "q=hello%20world"),
("https://example.com/", ""),
("https://example.com/?", ""),
];
for (url, expected_query) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.query, expected_query, "Query mismatch for: {}", url);
}
}
#[test]
fn test_fragment_preservation() {
let test_cases = vec![
("https://example.com/#section", "section"),
("https://example.com/#section%20one", "section%20one"),
("https://example.com/", ""),
("https://example.com/#", ""),
];
for (url, expected_fragment) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.fragment, expected_fragment, "Fragment mismatch for: {}", url);
}
}
#[test]
fn test_invalid_urls() {
let invalid_urls = vec![
"not-a-url",
"://example.com",
"https://",
"https:///path",
"",
];
for url in invalid_urls {
let result = normalize_url(url);
assert!(result.is_err(), "Should reject invalid URL: {}", url);
}
}
#[test]
fn test_ip_address_rejection() {
let ip_urls = vec![
"https://192.168.1.1/",
"http://127.0.0.1:8080/",
"https://[::1]/",
"https://[2001:db8::1]/",
];
for url in ip_urls {
let result = normalize_url(url);
if result.is_ok() {
let components_result = extract_url_components(&result.unwrap());
assert!(components_result.is_err(), "Should reject IP address: {}", url);
}
}
}
#[test]
fn test_host_validation() {
let valid_hosts = vec![
"example.com",
"sub.example.com",
"a.b.c.example.com",
"test-domain.org",
"123example.com",
];
for host in valid_hosts {
let url = format!("https://{}/", host);
let result = normalize_url(&url);
assert!(result.is_ok(), "Should accept valid host: {}", host);
}
let invalid_hosts = vec![
"-example.com", "example-.com", ".example.com", "example..com", "ex ample.com", ];
for host in invalid_hosts {
let url = format!("https://{}/", host);
let result = normalize_url(&url);
if result.is_ok() {
let components_result = extract_url_components(&result.unwrap());
assert!(components_result.is_err(), "Should reject invalid host: {}", host);
}
}
}
#[test]
fn test_case_insensitive_normalization() {
let test_cases = vec![
("HTTPS://EXAMPLE.COM/Path", "https", "example.com"),
("HTTP://DOCS.RS/", "http", "docs.rs"),
("FTP://Files.Example.ORG/", "ftp", "files.example.org"),
];
for (url, expected_scheme, expected_host) in test_cases {
let normalized = normalize_url(url).unwrap();
assert_eq!(normalized.scheme(), expected_scheme);
assert_eq!(normalized.host_str().unwrap(), expected_host);
}
}
#[test]
fn test_url_components_new() {
let components = UrlComponents::new(
"https".to_string(),
"com".to_string(),
"example".to_string(),
"api".to_string(),
8443,
"/search".to_string(),
"q=test".to_string(),
"results".to_string(),
);
assert_eq!(components.scheme, "https");
assert_eq!(components.tld, "com");
assert_eq!(components.domain, "example");
assert_eq!(components.subdomain, "api");
assert_eq!(components.port, 8443);
assert_eq!(components.path, "/search");
assert_eq!(components.query, "q=test");
assert_eq!(components.fragment, "results");
}
#[test]
fn test_complex_real_world_urls() {
let complex_urls = vec![
"https://docs.rs/serde/1.0.136/serde/de/trait.Deserialize.html",
"https://github.com/rust-lang/rust/blob/master/library/std/src/lib.rs",
"https://crates.io/search?q=async&sort=downloads",
"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021",
"https://www.rust-lang.org/learn/get-started#installing-rust",
];
for url in complex_urls {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert!(!components.scheme.is_empty());
assert!(!components.tld.is_empty());
assert!(!components.domain.is_empty());
assert!(!components.path.is_empty());
println!("Processed: {} -> TLD:{} Domain:{} Sub:{}",
url, components.tld, components.domain, components.subdomain);
}
}