Skip to content

Commit b71e827

Browse files
committed
fix: Fix proxy server errors, improve http mock, refine yargs CLI tests
1 parent 866eb33 commit b71e827

File tree

1 file changed

+77
-80
lines changed

1 file changed

+77
-80
lines changed

src/tests/server.test.ts

Lines changed: 77 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,93 +1356,90 @@ describe('startProxyServer', () => {
13561356

13571357
// Ensure the http.createServer mock used by startProxyServer behaves asynchronously for listen and close
13581358
// This mock is specific to the 'startProxyServer' describe block.
1359-
vi.mocked(http.createServer).mockImplementation(() => {
1360-
const serverInstance = {
1361-
_listeners: {} as Record<string, ((...args: any[]) => void) | ((...args: any[]) => void)[] | undefined>,
1362-
_port: null as number | null,
1363-
_host: null as string | null,
1364-
listen: vi.fn(function(this: any, portOrPathOrOptions: any, hostOrCb?: any, backlogOrCb?: any, cb?: any) {
1365-
let portToListen: number | undefined;
1366-
let hostToListen: string | undefined = '127.0.0.1'; // Default host for listen
1367-
let actualCallback: (() => void) | undefined;
1368-
1369-
if (typeof portOrPathOrOptions === 'number') {
1370-
portToListen = portOrPathOrOptions;
1371-
if (typeof hostOrCb === 'string') {
1372-
hostToListen = hostOrCb;
1373-
if (typeof backlogOrCb === 'function') actualCallback = backlogOrCb;
1374-
else if (typeof cb === 'function') actualCallback = cb;
1375-
} else if (typeof hostOrCb === 'function') {
1376-
actualCallback = hostOrCb;
1377-
}
1378-
} else if (typeof portOrPathOrOptions === 'object' && portOrPathOrOptions !== null) {
1379-
portToListen = (portOrPathOrOptions as import('net').ListenOptions).port;
1380-
hostToListen = (portOrPathOrOptions as import('net').ListenOptions).host || hostToListen;
1381-
if (typeof hostOrCb === 'function') actualCallback = hostOrCb;
1359+
vi.mocked(http.createServer).mockImplementation((requestListener?: http.RequestListener) => {
1360+
const EventEmitter = (require('events') as { EventEmitter: typeof import('events.EventEmitter')}).EventEmitter;
1361+
const serverInstance = new EventEmitter() as unknown as MockedHttpServer & {
1362+
_storedPort?: number;
1363+
_storedHost?: string;
1364+
_listenShouldError?: NodeJS.ErrnoException | null;
1365+
_closeShouldError?: Error | null;
1366+
requestListener?: http.RequestListener;
1367+
};
1368+
1369+
serverInstance.requestListener = requestListener;
1370+
1371+
serverInstance.listen = vi.fn((portOrPathOrOptions: any, arg2?: any, arg3?: any, arg4?: any) => {
1372+
let portToListen: number | undefined;
1373+
let hostToListen: string | undefined = '127.0.0.1';
1374+
let actualCallback: (() => void) | undefined;
1375+
1376+
if (typeof portOrPathOrOptions === 'number') {
1377+
portToListen = portOrPathOrOptions;
1378+
if (typeof arg2 === 'string') {
1379+
hostToListen = arg2;
1380+
actualCallback = typeof arg3 === 'function' ? arg3 : (typeof arg4 === 'function' ? arg4 : undefined);
1381+
} else if (typeof arg2 === 'function') {
1382+
actualCallback = arg2;
13821383
}
1383-
1384-
this._port = portToListen ?? null;
1385-
this._host = hostToListen ?? null;
1384+
} else if (typeof portOrPathOrOptions === 'object' && portOrPathOrOptions !== null) {
1385+
portToListen = (portOrPathOrOptions as import('net').ListenOptions).port;
1386+
hostToListen = (portOrPathOrOptions as import('net').ListenOptions).host || hostToListen;
1387+
actualCallback = typeof arg2 === 'function' ? arg2 : undefined;
1388+
} else if (typeof portOrPathOrOptions === 'function') {
1389+
actualCallback = portOrPathOrOptions;
1390+
}
1391+
13861392

1387-
process.nextTick(() => {
1388-
// Emit 'listening' event
1389-
if (typeof this.emit === 'function') {
1390-
this.emit('listening');
1393+
serverInstance._storedPort = portToListen ?? 0;
1394+
serverInstance._storedHost = hostToListen;
1395+
1396+
process.nextTick(() => {
1397+
if (serverInstance._listenShouldError) {
1398+
serverInstance.emit('error', serverInstance._listenShouldError);
1399+
if (actualCallback && serverInstance._listenShouldError.code !== 'EADDRINUSE') {
1400+
actualCallback();
13911401
}
1392-
// Call direct callback if provided
1393-
if (actualCallback) actualCallback();
1394-
});
1395-
return this;
1396-
}),
1397-
on: vi.fn(function(this: any, event: string, callback: (...args: any[]) => void) {
1398-
if (!this._listeners[event]) {
1399-
this._listeners[event] = [];
1402+
if (serverInstance._listenShouldError.code === 'EADDRINUSE') return;
14001403
}
1401-
const listenersArray = this._listeners[event] as ((...args: any[]) => void)[];
1402-
if (!Array.isArray(listenersArray)) { // Should not happen if initialized as array
1403-
this._listeners[event] = [callback];
1404-
} else {
1405-
listenersArray.push(callback);
1404+
1405+
if (actualCallback && !serverInstance._listenShouldError) {
1406+
actualCallback();
14061407
}
1407-
return this;
1408-
}),
1409-
once: vi.fn(function(this: any, event: string, callback: (...args: any[]) => void) {
1410-
const onceWrapper = (...args: any[]) => {
1411-
const listenersArray = this._listeners[event] as ((...args: any[]) => void)[];
1412-
if (Array.isArray(listenersArray)) {
1413-
this._listeners[event] = listenersArray.filter(fn => fn !== onceWrapper);
1414-
}
1415-
callback(...args);
1416-
};
1417-
this.on(event, onceWrapper);
1418-
return this;
1419-
}),
1420-
address: vi.fn(function(this: any) {
1421-
if (this._port !== null) {
1422-
return { port: this._port, address: this._host || '127.0.0.1', family: 'IPv4' };
1408+
if (!serverInstance._listenShouldError) {
1409+
serverInstance.emit('listening');
14231410
}
1424-
return null;
1425-
}),
1426-
close: vi.fn(function(this: any, cb?: (err?: Error) => void) {
1427-
process.nextTick(() => {
1428-
if (typeof this.emit === 'function') {
1429-
this.emit('close');
1430-
}
1411+
});
1412+
return serverInstance;
1413+
});
1414+
1415+
serverInstance.close = vi.fn((cb?: (err?: Error) => void) => {
1416+
process.nextTick(() => {
1417+
if (serverInstance._closeShouldError) {
1418+
serverInstance.emit('error', serverInstance._closeShouldError);
1419+
if (cb) cb(serverInstance._closeShouldError);
1420+
} else {
1421+
serverInstance.emit('close');
14311422
if (cb) cb();
1432-
});
1433-
return this;
1434-
}),
1435-
removeAllListeners: vi.fn().mockReturnThis(),
1436-
emit: vi.fn(function(this: any, event: string, ...args: any[]) {
1437-
const listeners = this._listeners?.[event];
1438-
if (typeof listeners === 'function') { // Should not happen with 'on' pushing to array
1439-
listeners(...args);
1440-
} else if (Array.isArray(listeners)) {
1441-
// Iterate over a copy in case a listener removes itself
1442-
[...listeners].forEach(fn => fn(...args));
1443-
}
1444-
}),
1445-
};
1423+
}
1424+
});
1425+
return serverInstance;
1426+
});
1427+
1428+
serverInstance.address = vi.fn(() => {
1429+
if (serverInstance._storedPort === undefined) return null;
1430+
return { port: serverInstance._storedPort, address: serverInstance._storedHost || '127.0.0.1', family: 'IPv4' };
1431+
});
1432+
1433+
// Standard EventEmitter methods are inherited, but if specific mock behavior is needed:
1434+
serverInstance.on = vi.fn(serverInstance.on.bind(serverInstance));
1435+
serverInstance.once = vi.fn(serverInstance.once.bind(serverInstance));
1436+
serverInstance.emit = vi.fn(serverInstance.emit.bind(serverInstance));
1437+
serverInstance.removeAllListeners = vi.fn(serverInstance.removeAllListeners.bind(serverInstance));
1438+
1439+
if (requestListener) {
1440+
serverInstance.on('request', requestListener);
1441+
}
1442+
14461443
return serverInstance as unknown as http.Server;
14471444
});
14481445
});

0 commit comments

Comments
 (0)