-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Problem
UIServer.close() calls this.httpServer.close() but doesn't wait for the server to actually finish closing:
// app.ts:86
close() {
if (this.httpServer) {
this.httpServer.close(); // async, but not awaited
}
this.httpServer = undefined;
return this;
}http.Server.close() is asynchronous — the OS doesn't release the port immediately. This causes EADDRINUSE errors in tests when tests run back-to-back, because the previous server's port hasn't been released yet.
Current Workaround
Tests now use supertest(app.app) (passing the Express application directly) instead of supertest(app.start()) (passing the listening HTTP server). This lets supertest create ephemeral servers on random ports, avoiding the port conflict entirely.
Suggested Fix
Make UIServer.close() return a Promise that resolves when the server is fully closed:
close(): Promise<void> {
return new Promise((resolve, reject) => {
if (this.httpServer) {
this.httpServer.close((err) => {
this.httpServer = undefined;
if (err) reject(err);
else resolve();
});
} else {
resolve();
}
});
}This would also benefit production graceful shutdown scenarios.
Context
Discovered during the Jest → Vitest migration (PR #12756). With done callbacks, the timing gap between tests masked the issue. With async/await, tests chain faster and expose the race condition.