fix(admin): ferme le serveur socket.io en fin de suite de test monitoring
Le hook `after` de admin-monitoring-socket.test.ts appelait seulement `broadcast.stop()` (qui ne fait que couper l'intervalle de sonde) et `prisma.$disconnect()`, mais ne fermait jamais le serveur socket.io ni le `http.Server` en écoute sur le port 0. socket.io garde alors vivants les timers ping d'engine.io et la socket d'écoute ; comme Mocha tourne sans `--exit` (.mocharc.json), ce seul handle en fuite empêche le process de se terminer une fois le dernier test passé. C'est ce qui bloquait le job `test` en CI : « N passing » affiché, puis plusieurs heures d'attente jusqu'au kill du runner (runs #80/#81). Hisse `io` au scope de la suite et le ferme dans `after` via `io.close()` — ce qui déconnecte les sockets restantes, stoppe les timers d'engine.io et ferme le `http.Server` attaché. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
13d92f8fa8
commit
23be1e4d04
1 changed files with 16 additions and 1 deletions
|
|
@ -51,6 +51,15 @@ function waitForEvent<T>(socket: ClientSocket, event: string, timeoutMs = 1500):
|
||||||
describe("Admin monitoring — WebSocket push", () => {
|
describe("Admin monitoring — WebSocket push", () => {
|
||||||
let baseUrl: string;
|
let baseUrl: string;
|
||||||
let broadcast: MonitoringBroadcastService;
|
let broadcast: MonitoringBroadcastService;
|
||||||
|
// Hoisted to suite scope (not a local in `before`) so `after` can shut
|
||||||
|
// it down. `broadcast.stop()` alone only clears the probe interval — the
|
||||||
|
// socket.io server keeps engine.io's ping timers and the listening
|
||||||
|
// http.Server (port 0) alive, and Mocha runs without `--exit`
|
||||||
|
// (`.mocharc.json`), so that one leaked handle stops the whole process
|
||||||
|
// from ever draining once the last test passes. That is exactly what
|
||||||
|
// stalled CI: the `test` job printed "N passing" and then hung for
|
||||||
|
// hours until the runner was killed.
|
||||||
|
let io: SocketIoServer;
|
||||||
const clientSockets: ClientSocket[] = [];
|
const clientSockets: ClientSocket[] = [];
|
||||||
|
|
||||||
// A real listening server, not just createApp()'s bare Express instance
|
// A real listening server, not just createApp()'s bare Express instance
|
||||||
|
|
@ -60,7 +69,7 @@ describe("Admin monitoring — WebSocket push", () => {
|
||||||
// free one, so this suite never collides with anything else listening.
|
// free one, so this suite never collides with anything else listening.
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const httpServer = createServer().httpServer;
|
const httpServer = createServer().httpServer;
|
||||||
const io = new SocketIoServer(httpServer, { cors: { origin: "*" } });
|
io = new SocketIoServer(httpServer, { cors: { origin: "*" } });
|
||||||
broadcast = new MonitoringBroadcastService(io);
|
broadcast = new MonitoringBroadcastService(io);
|
||||||
await new Promise<void>((resolve) => httpServer.listen(0, resolve));
|
await new Promise<void>((resolve) => httpServer.listen(0, resolve));
|
||||||
const { port } = httpServer.address() as AddressInfo;
|
const { port } = httpServer.address() as AddressInfo;
|
||||||
|
|
@ -70,6 +79,12 @@ describe("Admin monitoring — WebSocket push", () => {
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
broadcast.stop();
|
broadcast.stop();
|
||||||
|
// Disconnects any socket still open, stops engine.io's timers, and
|
||||||
|
// closes the http.Server socket.io was attached to — without this the
|
||||||
|
// suite never lets Node's event loop drain (see the `io` field's note).
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
io.close((err) => (err ? reject(err) : resolve()));
|
||||||
|
});
|
||||||
await prisma.$disconnect();
|
await prisma.$disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue