local config = require('vimpack.config') local installer = require('vimpack.installer') local manifest = require('vimpack.manifest') local plan = require('vimpack.plan') local reg = { kind = 'template', url_template = 'https://h/${name}-${version}.tar.gz' } local function cfg_with(plugins) return assert(config.normalize({ registry = reg, plugins = plugins })) end describe('plan', function() it('installs what is not on disk', function() local built = plan.build(cfg_with({ { name = 'a', version = '1' } }), manifest.empty(), {}) assert_eq(1, #built.install) assert_eq('not installed', built.install[1].reason) end) it('skips an install that already matches', function() local data = manifest.empty() manifest.set(data, 'a', { version = '1', sha256 = 'ff', opt = false }) local built = plan.build(cfg_with({ { name = 'a', version = '1' } }), data, { a = { opt = false, dir = '/x/a' } }) assert_eq(0, #built.install) assert_eq(0, #built.update) assert_eq(1, #built.skip) end) it('updates on a version bump', function() local data = manifest.empty() manifest.set(data, 'a', { version = '1', opt = false }) local built = plan.build(cfg_with({ { name = 'a', version = '2' } }), data, { a = { opt = false, dir = '/x/a' } }) assert_eq(1, #built.update) assert_match('1 -> 2', built.update[1].reason) end) it('updates when the spec sha256 differs from the installed one', function() local data = manifest.empty() manifest.set(data, 'a', { version = '1', sha256 = 'aa', opt = false }) local built = plan.build( cfg_with({ { name = 'a', version = '1', sha256 = 'bb' } }), data, { a = { opt = false, dir = '/x/a' } } ) assert_eq(1, #built.update) assert_match('sha256', built.update[1].reason) end) it('updates when a plugin moves between start and opt', function() local data = manifest.empty() manifest.set(data, 'a', { version = '1', opt = false }) local built = plan.build( cfg_with({ { name = 'a', version = '1', opt = true } }), data, { a = { opt = false, dir = '/x/start/a' } } ) assert_eq(1, #built.update) assert_match('opt/', built.update[1].reason) end) it('updates a directory with no manifest entry', function() local built = plan.build(cfg_with({ { name = 'a', version = '1' } }), manifest.empty(), { a = { opt = false, dir = '/x/a' } }) assert_eq(1, #built.update) assert_match('no manifest entry', built.update[1].reason) end) it('flags orphans on disk and in the manifest', function() local data = manifest.empty() manifest.set(data, 'ghost', { version = '9' }) local built = plan.build( cfg_with({ { name = 'a', version = '1' } }), data, { a = { opt = false, dir = '/x/a' }, old = { opt = true, dir = '/x/opt/old' } } ) assert_eq(2, #built.orphans) assert_eq('ghost', built.orphans[1].name) assert_eq('old', built.orphans[2].name) assert_eq('/x/opt/old', built.orphans[2].dir) end) end) describe('status_rows', function() it('reports one row per plugin plus orphans', function() local data = manifest.empty() manifest.set(data, 'a', { version = '1', opt = false }) local rows = plan.status_rows( cfg_with({ { name = 'a', version = '2' }, { name = 'b', version = '1' } }), data, { a = { opt = false, dir = '/x/a' }, gone = { opt = false, dir = '/x/gone' } } ) assert_eq(3, #rows) assert_eq({ 'a', 'b', 'gone' }, { rows[1].name, rows[2].name, rows[3].name }) assert_eq({ 'drift', 'missing', 'orphan' }, { rows[1].state, rows[2].state, rows[3].state }) assert_eq('1', rows[1].installed) assert_eq('2', rows[1].wanted) end) end) describe('run_tasks', function() it('runs every item and respects the concurrency cap', function() local items = { 1, 2, 3, 4, 5 } local inflight, peak, seen = 0, 0, {} local done_results installer.run_tasks(items, 2, function(item, cb) inflight = inflight + 1 peak = math.max(peak, inflight) vim.schedule(function() seen[#seen + 1] = item inflight = inflight - 1 cb(true) end) end, function(results) done_results = results end) vim.wait(1000, function() return done_results ~= nil end) assert_eq(5, #done_results) assert_eq(5, #seen) assert_true(peak <= 2, 'peak concurrency was ' .. peak) end) it('completes immediately on an empty list', function() local called = false installer.run_tasks({}, 4, function() end, function() called = true end) assert_true(called) end) it('carries failures through', function() local results installer.run_tasks({ 'a' }, 1, function(_, cb) cb(false, 'boom') end, function(r) results = r end) assert_eq('boom', results[1].err) end) end)