cc-libs/tests/ccpm.lua

416 lines
14 KiB
Lua

local createLibTest = require("/apis/libtest")
local createCcpm = require("/apis/libccpm")
local testlib = createLibTest({ ... })
local counter = 0
-- Fresh, isolated state + install sandbox per case (never touches real /trapos).
local function freshDirs()
counter = counter + 1
local stateDir = "/ccpm-test/state-" .. counter
local installRoot = "/ccpm-test/root-" .. counter
fs.delete(stateDir)
fs.delete(installRoot)
return stateDir, installRoot
end
-- Minimal stub of the CC `http` API backed by a url -> body map.
local function fakeHttp(routes)
return {
get = function(url)
local body = routes[url]
if not body then
return nil
end
return {
readAll = function()
return body
end,
close = function() end,
}
end,
}
end
local function giteaBase(name, branch)
return "https://git.trapcloud.fr/" .. name .. "/raw/branch/" .. branch .. "/"
end
testlib.test("registryBaseUrl resolves a gitea branch", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
testlib.assertEquals(
ccpm.registryBaseUrl({ name = "guillaumearm/cc-libs", type = "gitea", branch = "next" }),
"https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/"
)
testlib.assertEquals(
ccpm.descriptorUrl({ name = "guillaumearm/cc-libs", type = "gitea", branch = "next" }, "trapos-net"),
"https://git.trapcloud.fr/guillaumearm/cc-libs/raw/branch/next/packages/trapos-net/ccpm.json"
)
end)
testlib.test("registry urls resolve an http base", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
testlib.assertEquals(
ccpm.registryBaseUrl({ name = "http://example.com/repo", type = "http" }),
"http://example.com/repo/"
)
testlib.assertEquals(
ccpm.descriptorUrl({ name = "http://example.com/repo/", type = "http" }, "trapos-net"),
"http://example.com/repo/packages/trapos-net/ccpm.json"
)
end)
testlib.test("resolve orders dependencies before dependents", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/a/ccpm.json"] = textutils.serializeJSON({
name = "a",
version = "1",
dependencies = { "b", "c" },
files = {},
}),
[base .. "packages/b/ccpm.json"] = textutils.serializeJSON({
name = "b",
version = "1",
dependencies = { "c" },
files = {},
}),
[base .. "packages/c/ccpm.json"] = textutils.serializeJSON({
name = "c",
version = "1",
dependencies = {},
files = {},
}),
}
local ccpm = createCcpm({ stateDir = freshDirs(), http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
local ordered, err = ccpm.resolve("a")
testlib.assertTrue(ordered, tostring(err))
testlib.assertEquals(#ordered, 3)
testlib.assertEquals(ordered[1].name, "c")
testlib.assertEquals(ordered[2].name, "b")
testlib.assertEquals(ordered[3].name, "a")
end)
testlib.test("resolve reports a missing package", function()
local ccpm = createCcpm({ stateDir = freshDirs(), http = fakeHttp({}) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
local ordered, err = ccpm.resolve("ghost")
testlib.assertTrue(not ordered)
testlib.assertTrue(string.find(err, "not found", 1, true))
end)
testlib.test("resolve detects a dependency cycle", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/a/ccpm.json"] = textutils.serializeJSON({
name = "a",
version = "1",
dependencies = { "b" },
}),
[base .. "packages/b/ccpm.json"] = textutils.serializeJSON({
name = "b",
version = "1",
dependencies = { "a" },
}),
}
local ccpm = createCcpm({ stateDir = freshDirs(), http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
local ordered, err = ccpm.resolve("a")
testlib.assertTrue(not ordered)
testlib.assertTrue(string.find(err, "cycle", 1, true))
end)
testlib.test("install rejects an already-installed package", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeLock({ packages = { foo = { version = "1", files = {} } } })
local ok, msg = ccpm.install("foo", {})
testlib.assertTrue(not ok)
testlib.assertTrue(string.find(msg, "already installed", 1, true))
testlib.assertTrue(string.find(msg, "reinstall foo", 1, true))
end)
testlib.test("install downloads files and records the lock", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/trapos-core/ccpm.json"] = textutils.serializeJSON({
name = "trapos-core",
version = "1",
dependencies = {},
files = { "apis/eventloop.lua" },
}),
[base .. "packages/trapos-net/ccpm.json"] = textutils.serializeJSON({
name = "trapos-net",
version = "1",
dependencies = { "trapos-core" },
files = { "apis/net.lua" },
}),
[base .. "apis/eventloop.lua"] = "eventloop-body",
[base .. "apis/net.lua"] = "net-body",
}
local sd, root = freshDirs()
local ccpm = createCcpm({ stateDir = sd, installRoot = root, http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
local ok = ccpm.install("trapos-net", {})
testlib.assertTrue(ok)
testlib.assertTrue(fs.exists(root .. "/apis/net.lua"))
testlib.assertTrue(fs.exists(root .. "/apis/eventloop.lua"))
local f = fs.open(root .. "/apis/net.lua", "r")
local body = f.readAll()
f.close()
testlib.assertEquals(body, "net-body")
local lock = ccpm.readLock()
testlib.assertTrue(lock.packages["trapos-net"])
testlib.assertTrue(lock.packages["trapos-core"])
testlib.assertEquals(lock.packages["trapos-net"].registry, "me/repo")
end)
testlib.test("install downloads files from a gitea registry", function()
local base = giteaBase("guillaumearm/cc-libs", "next")
local routes = {
[base .. "packages/trapos-core/ccpm.json"] = textutils.serializeJSON({
name = "trapos-core",
version = "1",
dependencies = {},
files = { "apis/eventloop.lua" },
}),
[base .. "packages/trapos-net/ccpm.json"] = textutils.serializeJSON({
name = "trapos-net",
version = "1",
dependencies = { "trapos-core" },
files = { "apis/net.lua" },
}),
[base .. "apis/eventloop.lua"] = "eventloop-body",
[base .. "apis/net.lua"] = "net-body",
}
local sd, root = freshDirs()
local ccpm = createCcpm({ stateDir = sd, installRoot = root, http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "guillaumearm/cc-libs", type = "gitea", branch = "next" } } })
local ok = ccpm.install("trapos-net", {})
testlib.assertTrue(ok)
testlib.assertTrue(fs.exists(root .. "/apis/net.lua"))
testlib.assertTrue(fs.exists(root .. "/apis/eventloop.lua"))
local f = fs.open(root .. "/apis/net.lua", "r")
local body = f.readAll()
f.close()
testlib.assertEquals(body, "net-body")
local lock = ccpm.readLock()
testlib.assertEquals(lock.packages["trapos-net"].registry, "guillaumearm/cc-libs")
end)
testlib.test("installing trapos writes aggregated os state", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/trapos/ccpm.json"] = textutils.serializeJSON({
name = "trapos",
version = "1",
dependencies = { "trapos-net" },
files = {},
}),
[base .. "packages/trapos-core/ccpm.json"] = textutils.serializeJSON({
name = "trapos-core",
version = "1",
dependencies = {},
files = { "programs/ccpm.lua" },
}),
[base .. "packages/trapos-net/ccpm.json"] = textutils.serializeJSON({
name = "trapos-net",
version = "1",
dependencies = { "trapos-core" },
files = { "apis/net.lua" },
autostart = { "servers/ping-server" },
}),
[base .. "programs/ccpm.lua"] = "ccpm-body",
[base .. "apis/net.lua"] = "net-body",
}
local sd, root = freshDirs()
local ccpm = createCcpm({ stateDir = sd, installRoot = root, http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
testlib.assertTrue(ccpm.install("trapos", {}))
local f = fs.open(sd .. "/manifest.json", "r")
local manifest = textutils.unserializeJSON(f.readAll())
f.close()
testlib.assertEquals(manifest.version, "1")
testlib.assertEquals(#manifest.files, 2)
testlib.assertEquals(manifest.autostart[1], "servers/ping-server")
end)
testlib.test("registry add and remove round-trip", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeConfig({ registries = {} })
testlib.assertTrue(ccpm.addRegistry("foo/bar", { branch = "next" }))
local regs = ccpm.listRegistries()
testlib.assertEquals(#regs, 1)
testlib.assertEquals(regs[1].name, "foo/bar")
testlib.assertEquals(regs[1].branch, "next")
local dupOk = ccpm.addRegistry("foo/bar", {})
testlib.assertTrue(not dupOk)
testlib.assertTrue(ccpm.removeRegistry("foo/bar"))
testlib.assertEquals(#ccpm.listRegistries(), 0)
local rmOk = ccpm.removeRegistry("nope")
testlib.assertTrue(not rmOk)
end)
testlib.test("addRegistry defaults to a gitea registry", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeConfig({ registries = {} })
local ok, registry = ccpm.addRegistry("foo/bar", {})
testlib.assertTrue(ok)
testlib.assertEquals(registry.type, "gitea")
testlib.assertEquals(registry.branch, "master")
local regs = ccpm.listRegistries()
testlib.assertEquals(regs[1].type, "gitea")
testlib.assertEquals(ccpm.registryBaseUrl(regs[1]), "https://git.trapcloud.fr/foo/bar/raw/branch/master/")
-- An explicit type is still honored.
testlib.assertTrue(ccpm.addRegistry("http://example.com/repo", { type = "http" }))
local explicit = ccpm.listRegistries()[2]
testlib.assertEquals(explicit.type, "http")
end)
testlib.test("compareVersions treats padded zeros as equal", function()
-- compareVersions is internal; probe via available() status
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeCache({ packages = { pkg = { version = "1.0.0", registry = "r" } } })
ccpm.writeLock({ packages = { pkg = { version = "1.0", registry = "r", files = {}, dependencies = {} } } })
local avail = ccpm.available()
testlib.assertEquals(avail[1].status, "up-to-date")
end)
testlib.test("update writes a package cache from registries", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/index.json"] = textutils.serializeJSON({ packages = { foo = "1.0.0", bar = "2.0.0" } }),
}
local ccpm = createCcpm({ stateDir = freshDirs(), http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
local cache = ccpm.update()
testlib.assertEquals(cache.packages.foo.version, "1.0.0")
testlib.assertEquals(cache.packages.foo.registry, "me/repo")
local search = ccpm.search("ba")
testlib.assertEquals(#search, 1)
testlib.assertEquals(search[1].name, "bar")
end)
testlib.test("available marks cached packages by install status", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeCache({
packages = {
alpha = { version = "1.0.0", registry = "me/repo" },
beta = { version = "2.0.0", registry = "me/repo" },
gamma = { version = "1.0.0", registry = "me/repo" },
},
})
ccpm.writeLock({
packages = {
alpha = { version = "1.0.0", files = {}, dependencies = {} },
beta = { version = "1.0.0", files = {}, dependencies = {} },
},
})
local available = ccpm.available()
testlib.assertEquals(#available, 3)
testlib.assertEquals(available[1].name, "alpha")
testlib.assertEquals(available[1].status, "up-to-date")
testlib.assertEquals(available[2].name, "beta")
testlib.assertEquals(available[2].status, "updatable")
testlib.assertEquals(available[3].name, "gamma")
testlib.assertEquals(available[3].status, "available")
end)
testlib.test("upgrade reinstalls outdated packages from cache", function()
local base = giteaBase("me/repo", "master")
local routes = {
[base .. "packages/foo/ccpm.json"] = textutils.serializeJSON({
name = "foo",
version = "2.0.0",
dependencies = {},
files = { "programs/foo.lua" },
}),
[base .. "programs/foo.lua"] = "foo-v2",
}
local sd, root = freshDirs()
local ccpm = createCcpm({ stateDir = sd, installRoot = root, http = fakeHttp(routes) })
ccpm.writeConfig({ registries = { { name = "me/repo", type = "gitea", branch = "master" } } })
ccpm.writeCache({ packages = { foo = { version = "2.0.0", registry = "me/repo" } } })
ccpm.writeLock({
packages = {
foo = { version = "1.0.0", registry = "me/repo", files = { "programs/foo.lua" }, dependencies = {} },
},
})
local ok, upgraded = ccpm.upgrade({})
testlib.assertTrue(ok)
testlib.assertEquals(#upgraded, 1)
testlib.assertEquals(upgraded[1], "foo")
testlib.assertEquals(ccpm.readLock().packages.foo.version, "2.0.0")
local f = fs.open(root .. "/programs/foo.lua", "r")
local body = f.readAll()
f.close()
testlib.assertEquals(body, "foo-v2")
end)
testlib.test("upgrade requires a package cache", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeLock({ packages = { foo = { version = "1.0.0", files = {}, dependencies = {} } } })
local ok, err = ccpm.upgrade({})
testlib.assertTrue(not ok)
testlib.assertTrue(string.find(err, "ccpm update", 1, true))
end)
testlib.test("uninstall refuses a package with dependents", function()
local ccpm = createCcpm({ stateDir = freshDirs() })
ccpm.writeLock({
packages = {
["trapos-core"] = { version = "1", files = {}, dependencies = {} },
["trapos-net"] = { version = "1", files = {}, dependencies = { "trapos-core" } },
},
})
local ok, err = ccpm.uninstall("trapos-core", {})
testlib.assertTrue(not ok)
testlib.assertTrue(string.find(err, "required by", 1, true))
testlib.assertTrue(string.find(err, "trapos-net", 1, true))
end)
testlib.test("uninstall removes a leaf package and its files", function()
local sd, root = freshDirs()
fs.makeDir(root .. "/apis")
local f = fs.open(root .. "/apis/net.lua", "w")
f.write("x")
f.close()
local ccpm = createCcpm({ stateDir = sd, installRoot = root })
ccpm.writeLock({
packages = {
["trapos-core"] = { version = "1", files = {}, dependencies = {} },
["trapos-net"] = { version = "1", files = { "apis/net.lua" }, dependencies = { "trapos-core" } },
},
})
testlib.assertTrue(ccpm.uninstall("trapos-net", {}))
testlib.assertTrue(not fs.exists(root .. "/apis/net.lua"))
testlib.assertTrue(ccpm.readLock().packages["trapos-net"] == nil)
testlib.assertTrue(ccpm.readLock().packages["trapos-core"] ~= nil)
end)
testlib.run()