Files
pi-gui/test/recovery-supervisor.test.js
alex b7fea83ed6 feat(bridge): establish local pi status bridge
Deliver the initial local bridge, Noctalia v4/v5 adapters, desktop client, service unit, tests, and implementation documentation for persistent Pi status and control.
2026-07-27 14:51:45 +02:00

66 lines
2.0 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { createRecoverySupervisor } from "../src/bridge/recovery-supervisor.js";
test("restarts after bounded exponential delays and stops after exhaustion", async () => {
const delays = [];
const states = [];
let starts = 0;
const supervisor = createRecoverySupervisor({
start: async () => ({ attempt: ++starts }),
maxAttempts: 2,
delayForAttempt: (attempt) => attempt * 100,
sleep: async (delay) => delays.push(delay),
onState: (state) => states.push(state),
});
assert.deepEqual(await supervisor.handleUnexpectedExit(), { attempt: 1 });
assert.deepEqual(await supervisor.handleUnexpectedExit(), { attempt: 2 });
assert.equal(await supervisor.handleUnexpectedExit(), undefined);
assert.deepEqual(delays, [100, 200]);
assert.deepEqual(
states.map(({ state, attempt }) => ({ state, attempt })),
[
{ state: "recovering", attempt: 1 },
{ state: "healthy", attempt: 1 },
{ state: "recovering", attempt: 2 },
{ state: "healthy", attempt: 2 },
{ state: "failed", attempt: 2 },
],
);
});
test("restarts failed launches within the same bounded recovery budget", async () => {
let starts = 0;
const supervisor = createRecoverySupervisor({
start: async () => {
starts += 1;
if (starts === 1) throw new Error("temporary spawn failure");
return { attempt: starts };
},
maxAttempts: 2,
delayForAttempt: () => 0,
sleep: async () => {},
});
assert.deepEqual(await supervisor.handleUnexpectedExit(), { attempt: 2 });
assert.equal(starts, 2);
});
test("cancels scheduled recovery when stopped", async () => {
let resolveSleep;
const supervisor = createRecoverySupervisor({
start: async () => assert.fail("must not restart after stop"),
delayForAttempt: () => 1,
sleep: () =>
new Promise((resolve) => {
resolveSleep = resolve;
}),
});
const recovering = supervisor.handleUnexpectedExit();
supervisor.stop();
resolveSleep();
assert.equal(await recovering, undefined);
});