require "uri-test"
local URI = require "uri"

module("test.resolve", lunit.testcase, package.seeall)

-- Test data from RFC 3986.  The 'http' prefix has been changed throughout
-- to 'x-foo' so as not to trigger any scheme-specific normalization.
local resolve_tests = {
    -- 5.4.1.  Normal Examples
    ["g:h"]             = "g:h",
    ["g"]               = "x-foo://a/b/c/g",
    ["./g"]             = "x-foo://a/b/c/g",
    ["g/"]              = "x-foo://a/b/c/g/",
    ["/g"]              = "x-foo://a/g",
    ["//g"]             = "x-foo://g",
    ["?y"]              = "x-foo://a/b/c/d;p?y",
    ["g?y"]             = "x-foo://a/b/c/g?y",
    ["#s"]              = "x-foo://a/b/c/d;p?q#s",
    ["g#s"]             = "x-foo://a/b/c/g#s",
    ["g?y#s"]           = "x-foo://a/b/c/g?y#s",
    [";x"]              = "x-foo://a/b/c/;x",
    ["g;x"]             = "x-foo://a/b/c/g;x",
    ["g;x?y#s"]         = "x-foo://a/b/c/g;x?y#s",
    [""]                = "x-foo://a/b/c/d;p?q",
    ["."]               = "x-foo://a/b/c/",
    ["./"]              = "x-foo://a/b/c/",
    [".."]              = "x-foo://a/b/",
    ["../"]             = "x-foo://a/b/",
    ["../g"]            = "x-foo://a/b/g",
    ["../.."]           = "x-foo://a/",
    ["../../"]          = "x-foo://a/",
    ["../../g"]         = "x-foo://a/g",

    -- 5.4.2.  Abnormal Examples
    ["../../../g"]      = "x-foo://a/g",
    ["../../../../g"]   = "x-foo://a/g",
    ["/./g"]            = "x-foo://a/g",
    ["/../g"]           = "x-foo://a/g",
    ["g."]              = "x-foo://a/b/c/g.",
    [".g"]              = "x-foo://a/b/c/.g",
    ["g.."]             = "x-foo://a/b/c/g..",
    ["..g"]             = "x-foo://a/b/c/..g",
    ["./../g"]          = "x-foo://a/b/g",
    ["./g/."]           = "x-foo://a/b/c/g/",
    ["g/./h"]           = "x-foo://a/b/c/g/h",
    ["g/../h"]          = "x-foo://a/b/c/h",
    ["g;x=1/./y"]       = "x-foo://a/b/c/g;x=1/y",
    ["g;x=1/../y"]      = "x-foo://a/b/c/y",
    ["g?y/./x"]         = "x-foo://a/b/c/g?y/./x",
    ["g?y/../x"]        = "x-foo://a/b/c/g?y/../x",
    ["g#s/./x"]         = "x-foo://a/b/c/g#s/./x",
    ["g#s/../x"]        = "x-foo://a/b/c/g#s/../x",
    ["x-foo:g"]         = "x-foo:g",

    -- Some extra tests for good measure
    ["#foo?"]           = "x-foo://a/b/c/d;p?q#foo?",
    ["?#foo"]           = "x-foo://a/b/c/d;p?#foo",
}

local function test_abs_rel (base, uref, expect)
    local bad = false

    -- Test 'resolve' method with object as argument.
    local u = assert(URI:new(uref))
    local b = assert(URI:new(base))
    u:resolve(b)
    local got = tostring(u)
    if got ~= expect then
        bad = true
        print("URI:new(" .. uref .. "):resolve(URI:new(" .. base .. ") ===> " ..
              expect .. " (not " .. got .. ")")
    end

    -- Test 'resolve' method with string as argument.
    u = assert(URI:new(uref))
    u:resolve(base)
    local got = tostring(u)
    if got ~= expect then
        bad = true
        print("URI:new(" .. uref .. "):resolve(URI:new(" .. base .. ") ===> " ..
              expect .. " (not " .. got .. ")")
    end

    -- Test resolving relative URI using the constructor.
    local u = assert(URI:new(uref, base))
    local got = tostring(u)
    if got ~= expect then
        bad = true
        print("URI:new(" .. uref .. ", " .. base .. ") ==> " .. expect ..
              " (not " .. got .. ")")
    end

    return bad
end

function test_resolve ()
    local base = "x-foo://a/b/c/d;p?q"
    local testno = 1
    local bad = false

    for rel, abs in pairs(resolve_tests) do
        if test_abs_rel(base, rel, abs) then bad = true end
    end

    if bad then fail("one of the checks went wrong") end
end

function test_resolve_error ()
    local base = assert(URI:new("urn:oid:1.2.3"))
    local uri = assert(URI:new("not-valid-path-for-urn"))

    -- The 'resolve' method should throw an exception if the absolute URI
    -- that results from the resolution would be invalid.
    assert_error("calling resolve() creates invalid URI",
                 function () uri:resolve(base) end)
    assert_true(uri:is_relative())
    is("not-valid-path-for-urn", tostring(uri))

    -- But the constructor should return an error in its normal fashion.
    local ok, err = URI:new(uri, base)
    assert_nil(ok)
    assert_string(err)
end

local relativize_tests = {
    -- Empty path if the path is the same as the base URI's.
    { "http://ex/", "http://ex/", "" },
    { "http://ex/a/b", "http://ex/a/b", "" },
    { "http://ex/a/b/", "http://ex/a/b/", "" },
    -- Absolute path if the base URI's path doesn't help.
    { "http://ex/", "http://ex/a/b", "/" },
    { "http://ex/", "http://ex/a/b/", "/" },
    { "http://ex/x/y", "http://ex/", "/x/y" },
    { "http://ex/x/y/", "http://ex/", "/x/y/" },
    { "http://ex/x", "http://ex/a", "/x" },
    { "http://ex/x", "http://ex/a/", "/x" },
    { "http://ex/x/", "http://ex/a", "/x/" },
    { "http://ex/x/", "http://ex/a/", "/x/" },
    { "http://ex/x/y", "http://ex/a/b", "/x/y" },
    { "http://ex/x/y", "http://ex/a/b/", "/x/y" },
    { "http://ex/x/y/", "http://ex/a/b", "/x/y/" },
    { "http://ex/x/y/", "http://ex/a/b/", "/x/y/" },
    -- Add to the end of the base path.
    { "x-a://ex/a/b/c", "x-a://ex/a/b/", "c" },
    { "x-a://ex/a/b/c/", "x-a://ex/a/b/", "c/" },
    { "x-a://ex/a/b/c/d", "x-a://ex/a/b/", "c/d" },
    { "x-a://ex/a/b/c/d/", "x-a://ex/a/b/", "c/d/" },
    { "x-a://ex/a/b/c/d/e", "x-a://ex/a/b/", "c/d/e" },
    { "x-a://ex/a/b/c:foo/d/e", "x-a://ex/a/b/", "./c:foo/d/e" },
    -- Change last segment in base path, and add to it.
    { "x-a://ex/a/b/", "x-a://ex/a/b/c", "./" },
    { "x-a://ex/a/b/x", "x-a://ex/a/b/c", "x" },
    { "x-a://ex/a/b/x/", "x-a://ex/a/b/c", "x/" },
    { "x-a://ex/a/b/x/y", "x-a://ex/a/b/c", "x/y" },
    { "x-a://ex/a/b/x:foo/y", "x-a://ex/a/b/c", "./x:foo/y" },
    -- Use '..' segments.
    { "x-a://ex/a/b/c", "x-a://ex/a/b/c/d", "../c" },
    { "x-a://ex/a/b/c", "x-a://ex/a/b/c/", "../c" },
    { "x-a://ex/a/b/", "x-a://ex/a/b/c/", "../" },
    { "x-a://ex/a/b/", "x-a://ex/a/b/c/d", "../" },
    { "x-a://ex/a/b", "x-a://ex/a/b/c/", "../../b" },
    { "x-a://ex/a/b", "x-a://ex/a/b/c/d", "../../b" },
    { "x-a://ex/a/", "x-a://ex/a/b/c/", "../../" },
    { "x-a://ex/a/", "x-a://ex/a/b/c/d", "../../" },
    -- Preserve query and fragment parts.
    { "http://ex/a/b", "http://ex/a/b?baseq#basef", "b" },
    { "http://ex/a/b:c", "http://ex/a/b:c?baseq#basef", "./b:c" },
    { "http://ex/a/b?", "http://ex/a/b?baseq#basef", "?" },
    { "http://ex/a/b?foo", "http://ex/a/b?baseq#basef", "?foo" },
    { "http://ex/a/b?foo#", "http://ex/a/b?baseq#basef", "?foo#" },
    { "http://ex/a/b?foo#bar", "http://ex/a/b?baseq#basef", "?foo#bar" },
    { "http://ex/a/b#bar", "http://ex/a/b?baseq#basef", "b#bar" },
    { "http://ex/a/b:foo#bar", "http://ex/a/b:foo?baseq#basef", "./b:foo#bar" },
    { "http://ex/a/b:foo#bar", "http://ex/a/b:foo#basef", "#bar" },
}

function test_relativize ()
    for _, test in ipairs(relativize_tests) do
        local uri = assert(URI:new(test[1]))
        uri:relativize(test[2])
        is(test[3], tostring(uri))

        -- Make sure it will resolve back to the original value.
        uri:resolve(test[2])
        is(test[1], tostring(uri))
    end
end

function test_relativize_already_is ()
    local uri = assert(URI:new("../foo"))
    uri:relativize("http://host/")
    is("../foo", tostring(uri))
end

function test_relativize_urn ()
    local uri = assert(URI:new("urn:oid:1.2.3"))
    uri:relativize("urn:oid:1")
    is("urn:oid:1.2.3", tostring(uri))
end

-- vi:ts=4 sw=4 expandtab