use sxurl::*;
#[test]
fn test_spec_hash_vectors() {
assert_eq!(ComponentHasher::hash_tld("rs").unwrap(), 0x4817c7e); assert_eq!(ComponentHasher::hash_domain("docs").unwrap(), 0x16ca8efb818406f); assert_eq!(ComponentHasher::hash_subdomain("").unwrap(), 0x989781e3); assert_eq!(ComponentHasher::hash_path("/").unwrap(), 0x35884d71189f9); assert_eq!(ComponentHasher::hash_params("").unwrap(), 0x6b4de8c2); assert_eq!(ComponentHasher::hash_fragment("").unwrap(), 0x22060); }
#[test]
fn test_docs_rs_complete_sxurl() {
let url = "https://docs.rs/";
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
let sxurl_bytes = pack_sxurl(&components).unwrap();
let hex = sxurl_to_hex(&sxurl_bytes);
let expected = "004817c7e16ca8efb818406f000000000001bb35884d71189f90000000000000"; assert_eq!(hex, expected);
}
#[test]
fn test_header_construction() {
let url = "https://docs.rs/";
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
let sxurl_bytes = pack_sxurl(&components).unwrap();
let hex = sxurl_to_hex(&sxurl_bytes);
assert_eq!(&hex[0..2], "00"); }
#[test]
fn test_header_with_flags() {
let test_cases = vec![
("https://api.example.com/", "0", "80"), ("https://example.com/search?q=test", "0", "30"), ("https://example.com/#results", "0", "08"), ("https://example.com:8443/", "0", "40"), ("http://api.example.com:8080/search?q=test#results", "1", "f8"), ];
for (url, expected_scheme, expected_flags) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
let sxurl_bytes = pack_sxurl(&components).unwrap();
let hex = sxurl_to_hex(&sxurl_bytes);
assert_eq!(&hex[0..1], expected_scheme, "Scheme mismatch for URL: {}", url);
assert_eq!(&hex[32..34], expected_flags, "Flags mismatch for URL: {}", url);
}
}
#[test]
fn test_url_normalization() {
let test_cases = vec![
("HTTPS://EXAMPLE.COM/Path", "https://example.com/Path"),
("https://DOCS.RS/", "https://docs.rs/"),
("HTTP://Test.Example.ORG/", "http://test.example.org/"),
];
for (input, expected) in test_cases {
let normalized = normalize_url(input).unwrap();
assert_eq!(normalized.as_str(), expected);
}
}
#[test]
fn test_psl_domain_splitting() {
let test_cases = vec![
("example.com", ("com", "example", "")),
("api.example.com", ("com", "example", "api")),
("docs.rs", ("rs", "docs", "")),
("example.co.uk", ("co.uk", "example", "")),
("api.example.co.uk", ("co.uk", "example", "api")),
("v2.api.example.com", ("com", "example", "v2.api")),
];
for (host, (expected_tld, expected_domain, expected_sub)) in test_cases {
let (tld, domain, subdomain) = split_host_with_psl(host).unwrap();
assert_eq!(tld, expected_tld, "TLD mismatch for {}", host);
assert_eq!(domain, expected_domain, "Domain mismatch for {}", host);
assert_eq!(subdomain, expected_sub, "Subdomain mismatch for {}", host);
}
}
#[test]
fn test_port_handling() {
let test_cases = vec![
("https://example.com/", 443, false), ("http://example.com/", 80, false), ("https://example.com:443/", 443, false), ("https://example.com:8443/", 8443, true), ("http://example.com:8080/", 8080, true), ];
for (url, expected_port, port_flag_should_be_set) in test_cases {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.port, expected_port);
let sxurl_bytes = pack_sxurl(&components).unwrap();
let hex = sxurl_to_hex(&sxurl_bytes);
let flags = u8::from_str_radix(&hex[32..34], 16).unwrap();
let port_present = (flags & (1 << 6)) != 0;
assert_eq!(port_present, port_flag_should_be_set, "Port flag mismatch for {}", url);
}
}
#[test]
fn test_hash_consistency() {
let test_cases = vec![
("tld", "com"),
("domain", "example"),
("sub", "api"),
("path", "/search"),
("params", "q=test"),
("frag", "results"),
];
for (label, data) in test_cases {
let hash1 = hash_component(label, data.as_bytes(), 32).unwrap();
let hash2 = hash_component(label, data.as_bytes(), 32).unwrap();
assert_eq!(hash1, hash2, "Hash inconsistency for {}:'{}'", label, data);
}
}
#[test]
fn test_hash_label_separation() {
let data = "com";
let tld_hash = hash_component("tld", data.as_bytes(), 16).unwrap();
let domain_hash = hash_component("domain", data.as_bytes(), 16).unwrap();
assert_ne!(tld_hash, domain_hash, "Same data with different labels should produce different hashes");
}
#[test]
fn test_bit_width_masking() {
let hash1 = hash_component("test", b"data", 16).unwrap();
let hash2 = hash_component("test", b"data", 32).unwrap();
assert!(hash1 < (1u64 << 16), "16-bit hash should fit in 16 bits");
assert!(hash2 < (1u64 << 32), "32-bit hash should fit in 32 bits");
}
#[test]
fn test_hex_slice_positions() {
let url = "https://api.example.com:8443/search?q=test#results";
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
let sxurl_bytes = pack_sxurl(&components).unwrap();
let hex = sxurl_to_hex(&sxurl_bytes);
assert_eq!(hex.len(), 64, "SXURL should be exactly 64 hex characters");
let header = &hex[0..3];
let tld_slice = &hex[3..7];
let domain_slice = &hex[7..22];
let subdomain_slice = &hex[22..30];
let port_slice = &hex[30..34];
let path_slice = &hex[34..49];
let params_slice = &hex[49..58];
let fragment_slice = &hex[58..64];
assert_eq!(header.len(), 3);
assert_eq!(tld_slice.len(), 4);
assert_eq!(domain_slice.len(), 15);
assert_eq!(subdomain_slice.len(), 8);
assert_eq!(port_slice.len(), 4);
assert_eq!(path_slice.len(), 15);
assert_eq!(params_slice.len(), 9);
assert_eq!(fragment_slice.len(), 6);
}
#[test]
fn test_encoder_api() {
let url = "https://docs.rs/";
let sxurl_bytes = encode_url(url).unwrap();
assert_eq!(sxurl_bytes.len(), 32);
let sxurl_hex = encode_url_to_hex(url).unwrap();
assert_eq!(sxurl_hex.len(), 64);
assert_eq!(sxurl_hex, "004817c7e16ca8efb818406f000000000001bb35884d71189f90000000000000");
let encoder = SxurlEncoder::new();
let sxurl_bytes2 = encoder.encode(url).unwrap();
let sxurl_hex2 = encoder.encode_to_hex(url).unwrap();
assert_eq!(sxurl_bytes, sxurl_bytes2);
assert_eq!(sxurl_hex, sxurl_hex2);
}
#[test]
fn test_invalid_schemes() {
let invalid_urls = vec![
"ws://example.com/",
"fts://example.com/", "htps://example.com/", "file:///path/to/file",
"mailto:test@example.com",
];
for url in invalid_urls {
let result = normalize_url(url);
assert!(result.is_err(), "Should reject invalid scheme: {}", url);
}
}
#[test]
fn test_valid_schemes() {
let valid_urls = vec![
("https://example.com/", "https"),
("http://example.com/", "http"),
("ftp://example.com/", "ftp"),
];
for (url, expected_scheme) in valid_urls {
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.scheme, expected_scheme);
}
}
#[test]
fn test_empty_components() {
let url = "https://example.com/";
let normalized = normalize_url(url).unwrap();
let components = extract_url_components(&normalized).unwrap();
assert_eq!(components.subdomain, "");
assert_eq!(components.query, "");
assert_eq!(components.fragment, "");
assert!(ComponentHasher::hash_subdomain("").is_ok());
assert!(ComponentHasher::hash_params("").is_ok());
assert!(ComponentHasher::hash_fragment("").is_ok());
}
#[test]
fn test_idna_domains() {
let url = "https://café.com/test";
let normalized = normalize_url(url).unwrap();
assert!(normalized.host_str().unwrap().is_ascii());
}
#[test]
fn test_round_trip_consistency() {
let urls = vec![
"https://example.com/",
"https://api.example.com/search?q=test",
"http://docs.rs/",
"https://www.google.com/search?q=rust&hl=en",
"ftp://files.example.org/pub/",
];
for url in urls {
let sxurl1 = encode_url_to_hex(url).unwrap();
let sxurl2 = encode_url_to_hex(url).unwrap();
assert_eq!(sxurl1, sxurl2, "Round-trip inconsistency for {}", url);
}
}
#[test]
fn test_hex_conversion() {
let sxurl_bytes = [0u8; 32];
let hex = sxurl_to_hex(&sxurl_bytes);
assert_eq!(hex.len(), 64);
assert_eq!(hex, "0".repeat(64));
let parsed = hex_to_sxurl(&hex).unwrap();
assert_eq!(parsed, sxurl_bytes);
}
#[test]
fn test_hex_validation() {
let invalid_hex_strings = vec![
"123".to_string(), "g".to_owned() + &"0".repeat(63), "0".repeat(66), ];
for invalid_hex in &invalid_hex_strings {
let result = hex_to_sxurl(&invalid_hex);
assert!(result.is_err(), "Should reject invalid hex: {}", invalid_hex);
}
}
#[test]
fn test_complex_urls() {
let complex_urls = vec![
"https://api.github.com/repos/rust-lang/rust",
"https://docs.rs/serde/1.0.136/serde/",
"https://crates.io/search?q=async",
"https://www.rust-lang.org/learn/get-started#installing-rust",
"http://archive.ubuntu.com/ubuntu/dists/",
];
for url in complex_urls {
let result = encode_url_to_hex(url);
assert!(result.is_ok(), "Should handle complex URL: {}", url);
let hex = result.unwrap();
assert_eq!(hex.len(), 64);
assert!(hex.chars().all(|c| c.is_ascii_hexdigit()), "Should be valid hex");
}
}
#[test]
fn test_spec_compliance() {
let url = "https://docs.rs/";
let sxurl = encode_url_to_hex(url).unwrap();
let spec_expected = "004817c7e16ca8efb818406f000000000001bb35884d71189f90000000000000";
assert_eq!(sxurl, spec_expected, "Implementation must match SXURL v2 spec exactly");
}