287 lines
12 KiB
Lua
287 lines
12 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 ghBase(name, branch)
|
|
return 'https://raw.githubusercontent.com/' .. name .. '/' .. branch .. '/';
|
|
end
|
|
|
|
testlib.test('registryBaseUrl resolves a github branch', function()
|
|
local ccpm = createCcpm({ stateDir = freshDirs() });
|
|
testlib.assertEquals(
|
|
ccpm.registryBaseUrl({ name = 'guillaumearm/cc-libs', type = 'github', branch = 'next' }),
|
|
'https://raw.githubusercontent.com/guillaumearm/cc-libs/next/'
|
|
);
|
|
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 = ghBase('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 = 'github', 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 = 'github', 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 = ghBase('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 = 'github', 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 = ghBase('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 = 'github', 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('installing trapos writes aggregated os state', function()
|
|
local base = ghBase('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 = 'github', 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('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 = ghBase('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 = 'github', 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 = ghBase('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 = 'github', 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();
|