import { jest } from '@jest/globals'; // Mock the logs module jest.unstable_mockModule('../dist/utils/logs.js', () => ({ warn: jest.fn(), error: jest.fn() })); // Import modules after mocking const { getRequestURL, getRealIP } = await import('../dist/utils/network.js'); const logs = await import('../dist/utils/logs.js'); describe('Network utilities', () => { describe('getRequestURL', () => { test('should handle http URLs', () => { const request = { url: 'http://example.com/path' }; const result = getRequestURL(request); expect(result).toBeInstanceOf(URL); expect(result.href).toBe('http://example.com/path'); }); test('should handle https URLs', () => { const request = { url: 'https://example.com/path' }; const result = getRequestURL(request); expect(result).toBeInstanceOf(URL); expect(result.href).toBe('https://example.com/path'); }); test('should construct URL from path and host header', () => { const request = { url: '/path', headers: { host: 'example.com' } }; const result = getRequestURL(request); expect(result).toBeInstanceOf(URL); expect(result.href).toBe('http://example.com/path'); }); test('should use https when secure property is true', () => { const request = { url: '/path', secure: true, headers: { host: 'example.com' } }; const result = getRequestURL(request); expect(result.href).toBe('https://example.com/path'); }); test('should use https when x-forwarded-proto is https', () => { const request = { url: '/path', headers: { host: 'example.com', 'x-forwarded-proto': 'https' } }; const result = getRequestURL(request); expect(result.href).toBe('https://example.com/path'); }); test('should handle Fetch-style headers with get() method', () => { const request = { url: '/path', headers: { get: jest.fn((name) => { if (name === 'host') return 'example.com'; return null; }) } }; const result = getRequestURL(request); expect(result.href).toBe('http://example.com/path'); }); test('should return null for missing URL', () => { const request = {}; const result = getRequestURL(request); expect(result).toBeNull(); }); test('should return null for invalid URL', () => { const request = { url: '/test', headers: { host: 'localhost' } }; // Mock URL constructor to throw const originalURL = global.URL; global.URL = function() { throw new TypeError('Invalid URL'); }; const result = getRequestURL(request); expect(result).toBeNull(); expect(logs.warn).toHaveBeenCalled(); // Restore global.URL = originalURL; }); test('should handle non-Error objects in catch block', () => { const request = { url: '/test', headers: { host: 'localhost' } }; // Mock URL constructor to throw non-Error const originalURL = global.URL; global.URL = function() { throw "String error instead of Error object"; }; const result = getRequestURL(request); expect(result).toBeNull(); // Restore global.URL = originalURL; }); test('should handle array host headers in Express requests', () => { const request = { url: '/path', headers: { host: ['example.com', 'backup.com'] } // Array host }; const result = getRequestURL(request); expect(result.href).toBe('http://example.com/path'); }); test('should handle empty array host headers', () => { const request = { url: '/path', headers: { host: [] } // Empty array }; const result = getRequestURL(request); expect(result.href).toBe('http://localhost/path'); // Falls back to localhost }); test('should handle x-forwarded-host with Fetch-style headers', () => { const request = { url: '/path', headers: { get: jest.fn((name) => { if (name === 'host') return null; if (name === 'x-forwarded-host') return 'forwarded.example.com'; return null; }) } }; const result = getRequestURL(request); expect(result.href).toBe('http://forwarded.example.com/path'); }); test('should fallback to localhost for Fetch-style headers when all host headers are missing', () => { const request = { url: '/path', headers: { get: jest.fn(() => null) // All headers return null } }; const result = getRequestURL(request); expect(result.href).toBe('http://localhost/path'); }); test('should handle x-forwarded-host in Express headers', () => { const request = { url: '/path', headers: { 'x-forwarded-host': 'forwarded.example.com' // No host header } }; const result = getRequestURL(request); expect(result.href).toBe('http://forwarded.example.com/path'); }); test('should handle array x-forwarded-host headers', () => { const request = { url: '/path', headers: { 'x-forwarded-host': ['forwarded.example.com', 'backup.example.com'] } }; const result = getRequestURL(request); expect(result.href).toBe('http://forwarded.example.com/path'); }); test('should handle empty array x-forwarded-host headers', () => { const request = { url: '/path', headers: { 'x-forwarded-host': [] } }; const result = getRequestURL(request); expect(result.href).toBe('http://localhost/path'); }); }); describe('getRealIP', () => { test('should extract IP from x-forwarded-for header', () => { const request = { headers: { 'x-forwarded-for': '192.168.1.100' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should extract first IP from comma-separated x-forwarded-for', () => { const request = { headers: { 'x-forwarded-for': '192.168.1.100, 10.0.0.1, 127.0.0.1' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should use x-real-ip when x-forwarded-for is missing', () => { const request = { headers: { 'x-real-ip': '192.168.1.100' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should handle Fetch-style headers using get() method', () => { const request = { headers: { get: jest.fn((name) => { if (name === 'x-forwarded-for') return '192.168.1.100'; return null; }) } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should use server remoteAddress when headers are empty', () => { const request = { headers: {} }; const server = { remoteAddress: '192.168.1.100' }; const result = getRealIP(request, server); expect(result).toBe('192.168.1.100'); }); test('should use connection.remoteAddress for Express requests', () => { const request = { headers: {}, connection: { remoteAddress: '192.168.1.100' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should use req.ip property when available', () => { const request = { headers: {}, ip: '192.168.1.100' }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should clean IPv6 mapped IPv4 addresses', () => { const request = { headers: { 'x-forwarded-for': '::ffff:192.168.1.100' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should return 127.0.0.1 as ultimate fallback', () => { const request = { headers: {} }; const result = getRealIP(request); expect(result).toBe('127.0.0.1'); }); test('should prioritize x-forwarded-for over other sources', () => { const request = { headers: { 'x-forwarded-for': '192.168.1.100' }, connection: { remoteAddress: '10.0.0.1' }, ip: '172.16.0.1' }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should handle array x-forwarded-for headers', () => { const request = { headers: { 'x-forwarded-for': ['192.168.1.100', '10.0.0.1'] } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should handle array x-real-ip headers', () => { const request = { headers: { 'x-real-ip': ['192.168.1.100', '10.0.0.1'] } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should use x-real-ip with Fetch-style headers when x-forwarded-for is missing', () => { const request = { headers: { get: jest.fn((name) => { if (name === 'x-forwarded-for') return null; if (name === 'x-real-ip') return '192.168.1.100'; return null; }) } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should use socket.remoteAddress when connection.remoteAddress is missing', () => { const request = { headers: {}, connection: {}, // connection exists but no remoteAddress socket: { remoteAddress: '192.168.1.100' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should handle missing connection but present socket', () => { const request = { headers: {}, socket: { remoteAddress: '192.168.1.100' } }; const result = getRealIP(request); // Note: socket.remoteAddress is only checked within the connection block // When no connection exists, it falls back to URL hostname/127.0.0.1 expect(result).toBe('127.0.0.1'); }); test('should handle whitespace in comma-separated IPs', () => { const request = { headers: { 'x-forwarded-for': ' 192.168.1.100 , 10.0.0.1 , 127.0.0.1 ' } }; const result = getRealIP(request); expect(result).toBe('192.168.1.100'); }); test('should fallback to URL hostname when all IP sources fail', () => { const request = { headers: {}, url: '/test' }; const result = getRealIP(request); // getRequestURL constructs URL with localhost as default host, so hostname is 'localhost' expect(result).toBe('localhost'); }); test('should handle undefined IP values gracefully', () => { const request = { headers: { 'x-forwarded-for': undefined, 'x-real-ip': undefined } }; const result = getRealIP(request); expect(result).toBe('127.0.0.1'); }); test('should handle null IP values gracefully', () => { const request = { headers: { 'x-forwarded-for': null, 'x-real-ip': null } }; const result = getRealIP(request); expect(result).toBe('127.0.0.1'); }); test('should handle empty string IP values', () => { const request = { headers: { 'x-forwarded-for': '', 'x-real-ip': '' } }; const result = getRealIP(request); expect(result).toBe('127.0.0.1'); }); test('should handle empty array header values', () => { const request = { headers: { 'x-forwarded-for': [], 'x-real-ip': [] } }; const result = getRealIP(request); expect(result).toBe('127.0.0.1'); }); }); });