chore(lint): reduce relay test no-await-in-loop warnings

Refactor while-loops and for-retry loops in e2e.test.ts and
live-relay.e2e.test.ts into recursive poll/attempt helpers.
Remaining 8 warnings in encrypted-channel.ts concern the custom
Transport interface (on-handler slots, not DOM EventTarget) and a
sequential send loop; both reflect deliberate runtime contracts.
This commit is contained in:
Mohamed Boudra
2026-04-24 02:14:01 +07:00
parent edd92acb3d
commit e730d13957
2 changed files with 62 additions and 46 deletions

View File

@@ -92,17 +92,48 @@ async function waitForServer(
relayProcess: ChildProcess,
timeout = 15000,
): Promise<void> {
const start = Date.now();
while (Date.now() - start < timeout) {
const deadline = Date.now() + timeout;
async function poll(): Promise<void> {
if (Date.now() >= deadline) {
throw new Error(`Server did not start on port ${port} within ${timeout}ms`);
}
assertRelayStillRunning(relayProcess);
try {
await tryConnect(port);
return;
} catch {
await sleep(100);
return poll();
}
}
throw new Error(`Server did not start on port ${port} within ${timeout}ms`);
return poll();
}
function probeRelayWebSocket(port: number): Promise<boolean> {
const serverId = `probe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
const probeUrl = `ws://127.0.0.1:${port}/ws?serverId=${serverId}&role=server&v=2`;
return new Promise<boolean>((resolve) => {
const ws = new WebSocket(probeUrl);
let settled = false;
const settle = (value: boolean) => {
if (settled) return;
settled = true;
resolve(value);
};
const timer = setTimeout(() => {
ws.terminate();
settle(false);
}, 5000);
ws.once("open", () => {
clearTimeout(timer);
ws.close(1000, "probe");
settle(true);
});
ws.once("error", () => {
clearTimeout(timer);
settle(false);
});
});
}
async function waitForRelayWebSocketReady(
@@ -110,39 +141,28 @@ async function waitForRelayWebSocketReady(
relayProcess: ChildProcess,
timeout = 60000,
): Promise<void> {
const start = Date.now();
while (Date.now() - start < timeout) {
assertRelayStillRunning(relayProcess);
const serverId = `probe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
const probeUrl = `ws://127.0.0.1:${port}/ws?serverId=${serverId}&role=server&v=2`;
const opened = await new Promise<boolean>((resolve) => {
const ws = new WebSocket(probeUrl);
let settled = false;
const settle = (value: boolean) => {
if (settled) return;
settled = true;
resolve(value);
};
const timer = setTimeout(() => {
ws.terminate();
settle(false);
}, 5000);
ws.once("open", () => {
clearTimeout(timer);
ws.close(1000, "probe");
settle(true);
});
ws.once("error", () => {
clearTimeout(timer);
settle(false);
});
});
if (opened) {
return;
const deadline = Date.now() + timeout;
async function poll(): Promise<void> {
if (Date.now() >= deadline) {
throw new Error(`Relay WebSocket endpoint not ready on port ${port} within ${timeout}ms`);
}
assertRelayStillRunning(relayProcess);
const opened = await probeRelayWebSocket(port);
if (opened) return;
await sleep(250);
return poll();
}
throw new Error(`Relay WebSocket endpoint not ready on port ${port} within ${timeout}ms`);
return poll();
}
async function waitForProcessExit(
relayProcess: ChildProcess,
deadline: number,
): Promise<void> {
if (relayProcess.exitCode !== null) return;
if (Date.now() >= deadline) return;
await sleep(50);
return waitForProcessExit(relayProcess, deadline);
}
async function stopRelayProcess(relayProcess: ChildProcess): Promise<void> {
@@ -151,20 +171,14 @@ async function stopRelayProcess(relayProcess: ChildProcess): Promise<void> {
}
relayProcess.kill("SIGTERM");
const start = Date.now();
while (relayProcess.exitCode === null && Date.now() - start < SHUTDOWN_TIMEOUT_MS) {
await sleep(50);
}
await waitForProcessExit(relayProcess, Date.now() + SHUTDOWN_TIMEOUT_MS);
if (relayProcess.exitCode !== null) {
return;
}
relayProcess.kill("SIGKILL");
const killStart = Date.now();
while (relayProcess.exitCode === null && Date.now() - killStart < 2000) {
await sleep(50);
}
await waitForProcessExit(relayProcess, Date.now() + 2000);
if (relayProcess.exitCode === null) {
throw new Error("relay process did not exit after SIGTERM/SIGKILL");

View File

@@ -15,18 +15,20 @@ async function withRetry<T>(
fn: () => Promise<T>,
options: { retries: number; delayMs: number },
): Promise<T> {
let lastError: unknown;
for (let attempt = 0; attempt <= options.retries; attempt++) {
async function attempt(attemptNumber: number, lastError: unknown): Promise<T> {
if (attemptNumber > options.retries) {
throw lastError instanceof Error ? lastError : new Error(String(lastError));
}
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt < options.retries) {
if (attemptNumber < options.retries) {
await new Promise((r) => setTimeout(r, options.delayMs));
}
return attempt(attemptNumber + 1, error);
}
}
throw lastError instanceof Error ? lastError : new Error(String(lastError));
return attempt(0, null);
}
function waitOpen(ws: WebSocket, label: string): Promise<void> {