Installer une nouvelle version remplace les fichiers sur disque mais ne touche
pas le process en cours : l'ancienne instance gardait le port 7317, la version
fraîchement installée mourait sur EADDRINUSE avant son handshake, et le shell se
contentait d'un console.error suivi d'un app.quit(). Depuis le lanceur, cliquer
l'icône ne produisait donc rien du tout.
- Tout échec de démarrage ouvre un dialogue Retry / Show log / Quit
(start-failure.ts, texte pur et testé) et la sortie du daemon est conservée
dans <userData>/logs/daemon.log. Une mort du daemon APRÈS le handshake propose
de le relancer, au lieu de laisser une fenêtre morte à l'écran.
- Le port est diagnostiqué avant le spawn (port-guard.ts, empreinte
{pid, ownerPid, port}) : un daemon orphelin, dont l'Electron est mort, est
repris (SIGTERM puis SIGKILL, en attendant un bind réellement possible) ;
une instance vivante ou un tiers (service, npx) est annoncé avec l'action qui
débloque, et jamais tué. La reprise exige deux preuves, l'empreinte orpheline
ET l'identité du process (ps -ww), car un pidfile périmé peut désigner un pid
recyclé entre-temps par un programme quelconque.
- Une mise à jour installée à chaud est signalée avec « Restart now »
(upgrade-watch.ts), qui arrête le daemon avant app.relaunch() ; sans quoi le
lock d'instance unique renvoyait silencieusement sur la fenêtre de l'ancienne
version, et on croyait avoir migré.
- ARBORETUM_DESKTOP_PORT pour cohabiter avec un Arboretum qui occupe 7317 en
permanence (service installé, ou daemon lancé en terminal).
24 tests dans packages/desktop/test, et quatre scénarios rejoués en dev sous
xvfb-run avec profil isolé : port tenu par un tiers, orphelin repris puis SPA
servie, instance vivante laissée intacte, pid recyclé épargné.
40 lines
2.0 KiB
TypeScript
40 lines
2.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import { DaemonStartError, describeStartFailure } from '../src/main/start-failure';
|
||
|
||
describe('describeStartFailure', () => {
|
||
it('port tenu par une autre instance : renvoie vers le tray, pas vers une trace technique', () => {
|
||
const err = new DaemonStartError('port-busy-instance', 'port 7317 is already in use');
|
||
const { message, detail } = describeStartFailure(err, 7317);
|
||
expect(message).toBe('Arboretum is already running');
|
||
expect(detail).toContain('7317');
|
||
expect(detail).toContain('system tray');
|
||
});
|
||
|
||
it('port tenu par un tiers : nomme les suspects et l’échappatoire (variable d’env)', () => {
|
||
const err = new DaemonStartError('port-busy-foreign', 'port 7317 is already in use', 'EADDRINUSE');
|
||
const { message, detail } = describeStartFailure(err, 7317);
|
||
expect(message).toBe('Port 7317 is already in use');
|
||
expect(detail).toContain('arboretum install');
|
||
expect(detail).toContain('ARBORETUM_DESKTOP_PORT');
|
||
expect(detail).toContain('EADDRINUSE');
|
||
});
|
||
|
||
it('échec de handshake : conserve le message et la queue de journal', () => {
|
||
const err = new DaemonStartError('handshake', 'daemon exited before handshake (code 1)', 'boom\nbadaboum');
|
||
const { message, detail } = describeStartFailure(err, 7317);
|
||
expect(message).toBe('Arboretum could not start its local server');
|
||
expect(detail).toContain('daemon exited before handshake (code 1)');
|
||
expect(detail).toContain('badaboum');
|
||
});
|
||
|
||
it('erreur quelconque (hors DaemonStartError) reste affichable', () => {
|
||
expect(describeStartFailure(new Error('ENOENT node'), 7317).detail).toContain('ENOENT node');
|
||
expect(describeStartFailure('cassé', 7317).detail).toContain('cassé');
|
||
});
|
||
|
||
it('sans queue de journal, aucun bloc « Server output » vide', () => {
|
||
const detail = describeStartFailure(new DaemonStartError('handshake', 'nope'), 7317).detail;
|
||
expect(detail).not.toContain('Server output');
|
||
});
|
||
});
|