// test_uri_ref.mbt — Tests for RFC 3986 URI-reference parsing and resolution
// (uri_ref.mbt), including the official test vectors of RFC 3986 Section 5.4.
///|
/// Resolves `reference` against the RFC 3986 base and checks the result.
fn assert_resolves(reference : String, expected : String) -> Unit raise {
let got = unwrap_ok(
resolve_uri_reference_string("http://a/b/c/d;p?q", reference),
)
assert_str_eq(got, expected)
}
///|
test "parse_uri_reference decomposes every component" {
let uri = unwrap_ok(parse_uri_reference("http://example.com:8080/path?q=1#f"))
match uri.scheme() {
Some(s) => assert_str_eq(s, "http")
None => fail("scheme missing")
}
match uri.authority() {
Some(a) => assert_str_eq(a, "example.com:8080")
None => fail("authority missing")
}
assert_str_eq(uri.path(), "/path")
match uri.query() {
Some(q) => assert_str_eq(q, "q=1")
None => fail("query missing")
}
match uri.fragment() {
Some(f) => assert_str_eq(f, "f")
None => fail("fragment missing")
}
}
///|
test "parse_uri_reference of a relative reference has no scheme or authority" {
let uri = unwrap_ok(parse_uri_reference("../../g?y#s"))
assert_true(uri.scheme() is None)
assert_true(uri.authority() is None)
assert_str_eq(uri.path(), "../../g")
match uri.query() {
Some(q) => assert_str_eq(q, "y")
None => fail("query missing")
}
match uri.fragment() {
Some(f) => assert_str_eq(f, "s")
None => fail("fragment missing")
}
}
///|
test "parse_uri_reference of the empty string is empty" {
let uri = unwrap_ok(parse_uri_reference(""))
assert_str_eq(uri.to_string(), "")
assert_true(uri.scheme() is None)
assert_str_eq(uri.path(), "")
}
///|
test "recomposition round-trips the components" {
let s = "https://user:pass@host:8443/a/b%20c?d=e&f#g-h"
let uri = unwrap_ok(parse_uri_reference(s))
assert_str_eq(uri.to_string(), s)
}
///|
test "percent-encoding is preserved byte-for-byte" {
let uri = unwrap_ok(parse_uri_reference("/a%2Fb%20c"))
assert_str_eq(uri.path(), "/a%2Fb%20c")
}
///|
test "malformed percent-encoding is rejected" {
expect_err_kind(
parse_uri_reference("http://ex%ample"),
InvalidPercentEncoding,
)
expect_err_kind(parse_uri_reference("http://ex%2"), InvalidPercentEncoding)
expect_err_kind(parse_uri_reference("http://ex%GG"), InvalidPercentEncoding)
}
///|
test "percent-encoding is allowed in the authority" {
// reg-name and userinfo both permit pct-encoded octets (RFC 3986).
let uri = unwrap_ok(parse_uri_reference("http://%65xample.com/~u"))
match uri.authority() {
Some(a) => assert_str_eq(a, "%65xample.com")
None => fail("authority missing")
}
let user = unwrap_ok(parse_uri_reference("http://user%20name@host/x"))
match user.authority() {
Some(a) => assert_str_eq(a, "user%20name@host")
None => fail("authority missing")
}
}
///|
test "space and control characters in a URI are rejected" {
expect_err_kind(parse_uri_reference("http://exa mple.com"), InvalidToken)
expect_err_kind(parse_uri_reference("http://example.com/\u{1}"), InvalidToken)
}
///|
test "colon in the first path segment without a scheme is ambiguous" {
expect_err_kind(parse_uri_reference("1a:b"), InvalidToken)
}
///|
test "scheme must start with a letter" {
expect_err_kind(parse_uri_reference("1http://x"), InvalidToken)
}
///|
test "scheme characters are letters digits plus dot dash" {
let uri = unwrap_ok(parse_uri_reference("mailto:user@example.com"))
match uri.scheme() {
Some(s) => assert_str_eq(s, "mailto")
None => fail("scheme missing")
}
assert_str_eq(uri.path(), "user@example.com")
}
///|
test "IPv6 authority is accepted" {
let uri = unwrap_ok(parse_uri_reference("http://[::1]:8080/x"))
match uri.authority() {
Some(a) => assert_str_eq(a, "[::1]:8080")
None => fail("authority missing")
}
}
///|
test "remove_dot_segments collapses dot segments" {
assert_str_eq(remove_dot_segments("/a/b/c/./../../g"), "/a/g")
assert_str_eq(remove_dot_segments("mid/content=5/../6"), "mid/6")
assert_str_eq(remove_dot_segments("/a/./b/../c"), "/a/c")
assert_str_eq(remove_dot_segments("/a/b/../../"), "/")
assert_str_eq(remove_dot_segments(""), "")
assert_str_eq(remove_dot_segments("/"), "/")
}
// --- RFC 3986 Section 5.4.1: Normal Examples -------------------------------
///|
test "RFC 3986 5.4.1 normal examples" {
assert_resolves("g:h", "g:h")
assert_resolves("g", "http://a/b/c/g")
assert_resolves("./g", "http://a/b/c/g")
assert_resolves("g/", "http://a/b/c/g/")
assert_resolves("/g", "http://a/g")
assert_resolves("//g", "http://g")
assert_resolves("?y", "http://a/b/c/d;p?y")
assert_resolves("g?y", "http://a/b/c/g?y")
assert_resolves("#s", "http://a/b/c/d;p?q#s")
assert_resolves("g#s", "http://a/b/c/g#s")
assert_resolves("g?y#s", "http://a/b/c/g?y#s")
assert_resolves(";x", "http://a/b/c/;x")
assert_resolves("g;x", "http://a/b/c/g;x")
assert_resolves("g;x?y#s", "http://a/b/c/g;x?y#s")
assert_resolves("", "http://a/b/c/d;p?q")
assert_resolves(".", "http://a/b/c/")
assert_resolves("./", "http://a/b/c/")
assert_resolves("..", "http://a/b/")
assert_resolves("../", "http://a/b/")
assert_resolves("../g", "http://a/b/g")
assert_resolves("../..", "http://a/")
assert_resolves("../../", "http://a/")
assert_resolves("../../g", "http://a/g")
}
// --- RFC 3986 Section 5.4.2: Abnormal Examples ------------------------------
///|
test "RFC 3986 5.4.2 abnormal examples" {
assert_resolves("../../../g", "http://a/g")
assert_resolves("../../../../g", "http://a/g")
assert_resolves("/./g", "http://a/g")
assert_resolves("/../g", "http://a/g")
assert_resolves("g.", "http://a/b/c/g.")
assert_resolves(".g", "http://a/b/c/.g")
assert_resolves("g..", "http://a/b/c/g..")
assert_resolves("..g", "http://a/b/c/..g")
assert_resolves("./../g", "http://a/b/g")
assert_resolves("./g/.", "http://a/b/c/g/")
assert_resolves("g/./h", "http://a/b/c/g/h")
assert_resolves("g/../h", "http://a/b/c/h")
assert_resolves("g;x=1/./y", "http://a/b/c/g;x=1/y")
assert_resolves("g;x=1/../y", "http://a/b/c/y")
assert_resolves("g?y/./x", "http://a/b/c/g?y/./x")
assert_resolves("g?y/../x", "http://a/b/c/g?y/../x")
assert_resolves("g#s/./x", "http://a/b/c/g#s/./x")
assert_resolves("g#s/../x", "http://a/b/c/g#s/../x")
}
///|
test "resolve an absolute reference ignores the base entirely" {
let got = unwrap_ok(
resolve_uri_reference_string("http://a/b/c/d;p?q", "https://other/x"),
)
assert_str_eq(got, "https://other/x")
}
///|
test "resolve with an empty base path uses the authority" {
let got = unwrap_ok(resolve_uri_reference_string("http://a", "b"))
assert_str_eq(got, "http://a/b")
}
///|
test "resolve against a base without authority" {
let got = unwrap_ok(resolve_uri_reference_string("/base/path/x", "y"))
assert_str_eq(got, "/base/path/y")
}
///|
test "resolve preserves a new fragment and drops the base fragment" {
let got = unwrap_ok(
resolve_uri_reference_string("http://a/b?old#basefrag", "#new"),
)
assert_str_eq(got, "http://a/b?old#new")
}
///|
test "resolve returns errors for malformed inputs" {
expect_err_kind(
resolve_uri_reference_string("http://exa mple", "x"),
InvalidToken,
)
expect_err_kind(
resolve_uri_reference_string("http://a", "ex%ample"),
InvalidPercentEncoding,
)
}
///|
test "UriReference::empty is a well-formed empty reference" {
let e = UriReference::empty()
assert_str_eq(e.to_string(), "")
assert_true(e.scheme() is None)
assert_true(e.authority() is None)
assert_str_eq(e.path(), "")
}