import { describe, expect, test } from 'bun:test'; import { handleAptUpgrade } from './apt-upgrade'; describe('handleAptUpgrade', () => { test('runs every step in order when each succeeds, ending with the dispatcher restart', async () => { const seen: string[][] = []; const result = await handleAptUpgrade([], {}, (argv) => { seen.push(argv); return { status: 0 }; }); expect(result.success).toBe(true); expect(seen).toEqual([ ['sudo', 'apt-get', 'update'], ['sudo', 'apt-get', '-y', '--only-upgrade', 'install', 'celilo', 'celilo-bootstrap'], ['/usr/local/bin/celilo', 'system', 'migrate'], // celilo#604: without this, apt installs new code and the running // dispatcher keeps serving the old — celilo-mgr did so for 9 days. ['/usr/local/bin/celilo', 'events', 'restart-daemon'], ]); }); test('fails, and names what DID complete, when the dispatcher restart fails', async () => { const result = await handleAptUpgrade([], {}, (argv) => ({ status: argv.includes('restart-daemon') ? 1 : 0, })); // The packages ARE upgraded and the dispatcher is NOT on the new code. // Saying so in the failure is the acceptance condition — silence here is // what let a stale dispatcher pass for a successful upgrade. expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain('restart the event dispatcher'); expect(result.error).toContain('apt-get upgrade'); expect(result.error).toContain('apply DB migrations'); } }); test('stops at the first failing step and does not run later ones', async () => { const seen: string[][] = []; const result = await handleAptUpgrade([], {}, (argv) => { seen.push(argv); // Fail the apt upgrade step (index 1). return { status: argv.includes('install') ? 100 : 0 }; }); expect(result.success).toBe(false); if (!result.success) expect(result.error).toContain('apt-get upgrade'); // update + install ran; migrate did NOT. expect(seen).toHaveLength(2); }); });