fix(rustproxy): Use cooperative cancellation for background tasks, prune stale caches and metric entries, and switch tests to dynamic port allocation to avoid port conflicts
This commit is contained in:
70
test/helpers/port-allocator.ts
Normal file
70
test/helpers/port-allocator.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import * as net from 'net';
|
||||
|
||||
/**
|
||||
* Finds `count` free ports by binding to port 0 and reading the OS-assigned port.
|
||||
* All servers are opened simultaneously to guarantee uniqueness.
|
||||
* Returns an array of guaranteed-free ports.
|
||||
*/
|
||||
export async function findFreePorts(count: number): Promise<number[]> {
|
||||
const servers: net.Server[] = [];
|
||||
const ports: number[] = [];
|
||||
|
||||
// Open all servers simultaneously on port 0
|
||||
await Promise.all(
|
||||
Array.from({ length: count }, () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const server = net.createServer();
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const addr = server.address() as net.AddressInfo;
|
||||
ports.push(addr.port);
|
||||
servers.push(server);
|
||||
resolve();
|
||||
});
|
||||
server.on('error', reject);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Close all servers
|
||||
await Promise.all(
|
||||
servers.map(
|
||||
(server) => new Promise<void>((resolve) => server.close(() => resolve()))
|
||||
)
|
||||
);
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that all given ports are free (not listening).
|
||||
* Useful as a cleanup assertion at the end of tests.
|
||||
* Throws if any port is still in use.
|
||||
*/
|
||||
export async function assertPortsFree(ports: number[]): Promise<void> {
|
||||
const results = await Promise.all(
|
||||
ports.map(
|
||||
(port) =>
|
||||
new Promise<{ port: number; free: boolean }>((resolve) => {
|
||||
const client = net.connect({ port, host: '127.0.0.1' });
|
||||
client.on('connect', () => {
|
||||
client.destroy();
|
||||
resolve({ port, free: false });
|
||||
});
|
||||
client.on('error', () => {
|
||||
resolve({ port, free: true });
|
||||
});
|
||||
client.setTimeout(1000, () => {
|
||||
client.destroy();
|
||||
resolve({ port, free: true });
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const occupied = results.filter((r) => !r.free);
|
||||
if (occupied.length > 0) {
|
||||
throw new Error(
|
||||
`Ports still in use after cleanup: ${occupied.map((r) => r.port).join(', ')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user