start fixing tests

This commit is contained in:
Juergen Kunz
2025-06-06 07:40:59 +00:00
parent 18d79ac7e1
commit b9be6533ae
15 changed files with 330 additions and 477 deletions

View File

@ -94,12 +94,13 @@ tap.test('PathMatcher - findAllMatches', async () => {
const matches = PathMatcher.findAllMatches(patterns, '/api/users/123/profile');
// All patterns should match (including /api/users as a prefix match)
expect(matches).toHaveLength(5);
// With the stricter path matching, /api/users won't match /api/users/123/profile
// Only patterns with wildcards, parameters, or exact matches will work
expect(matches).toHaveLength(4);
// Verify all expected patterns are in the results
const matchedPatterns = matches.map(m => m.pattern);
expect(matchedPatterns).toContain('/api/users');
expect(matchedPatterns).not.toContain('/api/users'); // This won't match anymore (no prefix matching)
expect(matchedPatterns).toContain('/api/users/:id');
expect(matchedPatterns).toContain('/api/users/:id/profile');
expect(matchedPatterns).toContain('/api/*');

View File

@ -1,110 +0,0 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as routeUtils from '../../../ts/core/utils/route-utils.js';
// Test domain matching
tap.test('Route Utils - Domain Matching - exact domains', async () => {
expect(routeUtils.matchDomain('example.com', 'example.com')).toEqual(true);
});
tap.test('Route Utils - Domain Matching - wildcard domains', async () => {
expect(routeUtils.matchDomain('*.example.com', 'sub.example.com')).toEqual(true);
expect(routeUtils.matchDomain('*.example.com', 'another.sub.example.com')).toEqual(true);
expect(routeUtils.matchDomain('*.example.com', 'example.com')).toEqual(false);
});
tap.test('Route Utils - Domain Matching - case insensitivity', async () => {
expect(routeUtils.matchDomain('example.com', 'EXAMPLE.com')).toEqual(true);
});
tap.test('Route Utils - Domain Matching - multiple domain patterns', async () => {
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'example.com')).toEqual(true);
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'sub.test.com')).toEqual(true);
expect(routeUtils.matchRouteDomain(['example.com', '*.test.com'], 'something.else')).toEqual(false);
});
// Test path matching
tap.test('Route Utils - Path Matching - exact paths', async () => {
expect(routeUtils.matchPath('/api/users', '/api/users')).toEqual(true);
});
tap.test('Route Utils - Path Matching - wildcard paths', async () => {
expect(routeUtils.matchPath('/api/*', '/api/users')).toEqual(true);
expect(routeUtils.matchPath('/api/*', '/api/products')).toEqual(true);
expect(routeUtils.matchPath('/api/*', '/something/else')).toEqual(false);
});
tap.test('Route Utils - Path Matching - complex wildcard patterns', async () => {
expect(routeUtils.matchPath('/api/*/details', '/api/users/details')).toEqual(true);
expect(routeUtils.matchPath('/api/*/details', '/api/products/details')).toEqual(true);
expect(routeUtils.matchPath('/api/*/details', '/api/users/other')).toEqual(false);
});
// Test IP matching
tap.test('Route Utils - IP Matching - exact IPs', async () => {
expect(routeUtils.matchIpPattern('192.168.1.1', '192.168.1.1')).toEqual(true);
});
tap.test('Route Utils - IP Matching - wildcard IPs', async () => {
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.1.100')).toEqual(true);
expect(routeUtils.matchIpPattern('192.168.1.*', '192.168.2.1')).toEqual(false);
});
tap.test('Route Utils - IP Matching - CIDR notation', async () => {
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.1.100')).toEqual(true);
expect(routeUtils.matchIpPattern('192.168.1.0/24', '192.168.2.1')).toEqual(false);
});
tap.test('Route Utils - IP Matching - IPv6-mapped IPv4 addresses', async () => {
expect(routeUtils.matchIpPattern('192.168.1.1', '::ffff:192.168.1.1')).toEqual(true);
});
tap.test('Route Utils - IP Matching - IP authorization with allow/block lists', async () => {
// With allow and block lists
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'], ['192.168.1.5'])).toEqual(true);
expect(routeUtils.isIpAuthorized('192.168.1.5', ['192.168.1.*'], ['192.168.1.5'])).toEqual(false);
// With only allow list
expect(routeUtils.isIpAuthorized('192.168.1.1', ['192.168.1.*'])).toEqual(true);
expect(routeUtils.isIpAuthorized('192.168.2.1', ['192.168.1.*'])).toEqual(false);
// With only block list
expect(routeUtils.isIpAuthorized('192.168.1.5', undefined, ['192.168.1.5'])).toEqual(false);
expect(routeUtils.isIpAuthorized('192.168.1.1', undefined, ['192.168.1.5'])).toEqual(true);
// With wildcard in allow list
expect(routeUtils.isIpAuthorized('192.168.1.1', ['*'], ['192.168.1.5'])).toEqual(true);
});
// Test route specificity calculation
tap.test('Route Utils - Route Specificity - calculating correctly', async () => {
const basicRoute = { domains: 'example.com' };
const pathRoute = { domains: 'example.com', path: '/api' };
const wildcardPathRoute = { domains: 'example.com', path: '/api/*' };
const headerRoute = { domains: 'example.com', headers: { 'content-type': 'application/json' } };
const complexRoute = {
domains: 'example.com',
path: '/api',
headers: { 'content-type': 'application/json' },
clientIp: ['192.168.1.1']
};
// Path routes should have higher specificity than domain-only routes
expect(routeUtils.calculateRouteSpecificity(pathRoute) >
routeUtils.calculateRouteSpecificity(basicRoute)).toEqual(true);
// Exact path routes should have higher specificity than wildcard path routes
expect(routeUtils.calculateRouteSpecificity(pathRoute) >
routeUtils.calculateRouteSpecificity(wildcardPathRoute)).toEqual(true);
// Routes with headers should have higher specificity than routes without
expect(routeUtils.calculateRouteSpecificity(headerRoute) >
routeUtils.calculateRouteSpecificity(basicRoute)).toEqual(true);
// Complex routes should have the highest specificity
expect(routeUtils.calculateRouteSpecificity(complexRoute) >
routeUtils.calculateRouteSpecificity(pathRoute)).toEqual(true);
expect(routeUtils.calculateRouteSpecificity(complexRoute) >
routeUtils.calculateRouteSpecificity(headerRoute)).toEqual(true);
});
export default tap.start();

View File

@ -54,7 +54,7 @@ tap.test('should detect and forward non-TLS connections on useHttpProxy ports',
findMatchingRoute: (criteria: any) => ({
route: mockSettings.routes[0]
}),
getAllRoutes: () => mockSettings.routes,
getRoutes: () => mockSettings.routes,
getRoutesForPort: (port: number) => mockSettings.routes.filter(r => {
const ports = Array.isArray(r.match.ports) ? r.match.ports : [r.match.ports];
return ports.some(p => {
@ -182,7 +182,7 @@ tap.test('should handle TLS connections normally', async (tapTest) => {
findMatchingRoute: (criteria: any) => ({
route: mockSettings.routes[0]
}),
getAllRoutes: () => mockSettings.routes,
getRoutes: () => mockSettings.routes,
getRoutesForPort: (port: number) => mockSettings.routes.filter(r => {
const ports = Array.isArray(r.match.ports) ? r.match.ports : [r.match.ports];
return ports.some(p => {

View File

@ -34,6 +34,7 @@ tap.test('should detect and forward non-TLS connections on HttpProxy ports', asy
};
proxy['httpProxyBridge'].stop = async () => {
console.log('Mock: HttpProxyBridge stopped');
return Promise.resolve(); // Ensure it returns a resolved promise
};
await proxy.start();
@ -44,11 +45,14 @@ tap.test('should detect and forward non-TLS connections on HttpProxy ports', asy
forwardedToHttpProxy = true;
connectionPath = 'httpproxy';
console.log('Mock: Connection forwarded to HttpProxy with args:', args[0], 'on port:', args[2]?.localPort);
// Just close the connection for the test
args[1].end(); // socket.end()
// Properly close the connection for the test
const socket = args[1];
socket.end();
socket.destroy();
};
// No need to mock getHttpProxy - the bridge already handles HttpProxy availability
// Mock getHttpProxy to indicate HttpProxy is available
(proxy as any).httpProxyBridge.getHttpProxy = () => ({ available: true });
// Make a connection to port 8080
const client = new net.Socket();
@ -73,13 +77,16 @@ tap.test('should detect and forward non-TLS connections on HttpProxy ports', asy
expect(connectionPath).toEqual('httpproxy');
client.destroy();
// Restore original method before stopping
(proxy as any).httpProxyBridge.forwardToHttpProxy = originalForward;
console.log('About to stop proxy...');
await proxy.stop();
console.log('Proxy stopped');
// Wait a bit to ensure port is released
await new Promise(resolve => setTimeout(resolve, 100));
// Restore original method
(proxy as any).httpProxyBridge.forwardToHttpProxy = originalForward;
});
// Test that verifies the fix detects non-TLS connections
@ -123,8 +130,10 @@ tap.test('should properly detect non-TLS connections on HttpProxy ports', async
proxy['httpProxyBridge'].forwardToHttpProxy = async function(...args: any[]) {
httpProxyForwardCalled = true;
console.log('HttpProxy forward called with connectionId:', args[0]);
// Just end the connection
args[1].end();
// Properly close the connection
const socket = args[1];
socket.end();
socket.destroy();
};
// Mock HttpProxyBridge methods
@ -136,6 +145,7 @@ tap.test('should properly detect non-TLS connections on HttpProxy ports', async
};
proxy['httpProxyBridge'].stop = async () => {
console.log('Mock: HttpProxyBridge stopped');
return Promise.resolve(); // Ensure it returns a resolved promise
};
// Mock getHttpProxy to return a truthy value

View File

@ -63,9 +63,21 @@ tap.test('should forward HTTP connections on port 8080', async (tapTest) => {
}
};
console.log('Making HTTP request to proxy...');
const response = await new Promise<http.IncomingMessage>((resolve, reject) => {
const req = http.request(options, (res) => resolve(res));
req.on('error', reject);
const req = http.request(options, (res) => {
console.log('Got response from proxy:', res.statusCode);
resolve(res);
});
req.on('error', (err) => {
console.error('Request error:', err);
reject(err);
});
req.setTimeout(5000, () => {
console.error('Request timeout');
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
@ -85,6 +97,9 @@ tap.test('should forward HTTP connections on port 8080', async (tapTest) => {
await new Promise<void>((resolve) => {
targetServer.close(() => resolve());
});
// Wait a bit to ensure port is fully released
await new Promise(resolve => setTimeout(resolve, 500));
});
tap.test('should handle basic HTTP request forwarding', async (tapTest) => {
@ -135,15 +150,30 @@ tap.test('should handle basic HTTP request forwarding', async (tapTest) => {
}
};
console.log('Making HTTP request to proxy...');
const response = await new Promise<http.IncomingMessage>((resolve, reject) => {
const req = http.request(options, (res) => resolve(res));
req.on('error', reject);
const req = http.request(options, (res) => {
console.log('Got response from proxy:', res.statusCode);
resolve(res);
});
req.on('error', (err) => {
console.error('Request error:', err);
reject(err);
});
req.setTimeout(5000, () => {
console.error('Request timeout');
req.destroy();
reject(new Error('Request timeout'));
});
req.end();
});
let responseData = '';
response.setEncoding('utf8');
response.on('data', chunk => responseData += chunk);
response.on('data', chunk => {
console.log('Received data chunk:', chunk);
responseData += chunk;
});
await new Promise(resolve => response.on('end', resolve));
expect(response.statusCode).toEqual(200);
@ -154,6 +184,9 @@ tap.test('should handle basic HTTP request forwarding', async (tapTest) => {
await new Promise<void>((resolve) => {
targetServer.close(() => resolve());
});
// Wait a bit to ensure port is fully released
await new Promise(resolve => setTimeout(resolve, 500));
});
tap.start();
export default tap.start();

View File

@ -82,13 +82,16 @@ tap.test('setup HttpProxy function-based targets test environment', async (tools
// Test static host/port routes
tap.test('should support static host/port routes', async () => {
// Get proxy port first
const proxyPort = httpProxy.getListeningPort();
const routes: IRouteConfig[] = [
{
name: 'static-route',
priority: 100,
match: {
domains: 'example.com',
ports: 0
ports: proxyPort
},
action: {
type: 'forward',
@ -102,9 +105,6 @@ tap.test('should support static host/port routes', async () => {
await httpProxy.updateRouteConfigs(routes);
// Get proxy port using the improved getListeningPort() method
const proxyPort = httpProxy.getListeningPort();
// Make request to proxy
const response = await makeRequest({
hostname: 'localhost',
@ -124,13 +124,14 @@ tap.test('should support static host/port routes', async () => {
// Test function-based host
tap.test('should support function-based host', async () => {
const proxyPort = httpProxy.getListeningPort();
const routes: IRouteConfig[] = [
{
name: 'function-host-route',
priority: 100,
match: {
domains: 'function.example.com',
ports: 0
ports: proxyPort
},
action: {
type: 'forward',
@ -147,9 +148,6 @@ tap.test('should support function-based host', async () => {
await httpProxy.updateRouteConfigs(routes);
// Get proxy port using the improved getListeningPort() method
const proxyPort = httpProxy.getListeningPort();
// Make request to proxy
const response = await makeRequest({
hostname: 'localhost',
@ -169,13 +167,14 @@ tap.test('should support function-based host', async () => {
// Test function-based port
tap.test('should support function-based port', async () => {
const proxyPort = httpProxy.getListeningPort();
const routes: IRouteConfig[] = [
{
name: 'function-port-route',
priority: 100,
match: {
domains: 'function-port.example.com',
ports: 0
ports: proxyPort
},
action: {
type: 'forward',
@ -192,9 +191,6 @@ tap.test('should support function-based port', async () => {
await httpProxy.updateRouteConfigs(routes);
// Get proxy port using the improved getListeningPort() method
const proxyPort = httpProxy.getListeningPort();
// Make request to proxy
const response = await makeRequest({
hostname: 'localhost',
@ -214,13 +210,14 @@ tap.test('should support function-based port', async () => {
// Test function-based host AND port
tap.test('should support function-based host AND port', async () => {
const proxyPort = httpProxy.getListeningPort();
const routes: IRouteConfig[] = [
{
name: 'function-both-route',
priority: 100,
match: {
domains: 'function-both.example.com',
ports: 0
ports: proxyPort
},
action: {
type: 'forward',
@ -238,9 +235,6 @@ tap.test('should support function-based host AND port', async () => {
await httpProxy.updateRouteConfigs(routes);
// Get proxy port using the improved getListeningPort() method
const proxyPort = httpProxy.getListeningPort();
// Make request to proxy
const response = await makeRequest({
hostname: 'localhost',
@ -260,13 +254,14 @@ tap.test('should support function-based host AND port', async () => {
// Test context-based routing with path
tap.test('should support context-based routing with path', async () => {
const proxyPort = httpProxy.getListeningPort();
const routes: IRouteConfig[] = [
{
name: 'context-path-route',
priority: 100,
match: {
domains: 'context.example.com',
ports: 0
ports: proxyPort
},
action: {
type: 'forward',
@ -287,9 +282,6 @@ tap.test('should support context-based routing with path', async () => {
await httpProxy.updateRouteConfigs(routes);
// Get proxy port using the improved getListeningPort() method
const proxyPort = httpProxy.getListeningPort();
// Make request to proxy with /api path
const apiResponse = await makeRequest({
hostname: 'localhost',

View File

@ -1,10 +1,10 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as tsclass from '@tsclass/tsclass';
import * as http from 'http';
import { ProxyRouter, type RouterResult } from '../ts/routing/router/proxy-router.js';
import { HttpRouter, type RouterResult } from '../ts/routing/router/http-router.js';
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
// Test proxies and configurations
let router: ProxyRouter;
let router: HttpRouter;
// Sample hostname for testing
const TEST_DOMAIN = 'example.com';
@ -23,33 +23,40 @@ function createMockRequest(host: string, url: string = '/'): http.IncomingMessag
return req;
}
// Helper: Creates a test proxy configuration
function createProxyConfig(
// Helper: Creates a test route configuration
function createRouteConfig(
hostname: string,
destinationIp: string = '10.0.0.1',
destinationPort: number = 8080
): tsclass.network.IReverseProxyConfig {
): IRouteConfig {
return {
hostName: hostname,
publicKey: 'mock-cert',
privateKey: 'mock-key',
destinationIps: [destinationIp],
destinationPorts: [destinationPort],
} as tsclass.network.IReverseProxyConfig;
name: `route-${hostname}`,
match: {
domains: [hostname],
ports: 443
},
action: {
type: 'forward',
target: {
host: destinationIp,
port: destinationPort
}
}
};
}
// SETUP: Create a ProxyRouter instance
tap.test('setup proxy router test environment', async () => {
router = new ProxyRouter();
// SETUP: Create an HttpRouter instance
tap.test('setup http router test environment', async () => {
router = new HttpRouter();
// Initialize with empty config
router.setNewProxyConfigs([]);
router.updateRoutes([]);
});
// Test basic routing by hostname
tap.test('should route requests by hostname', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
const config = createRouteConfig(TEST_DOMAIN);
router.updateRoutes([config]);
const req = createMockRequest(TEST_DOMAIN);
const result = router.routeReq(req);
@ -60,8 +67,8 @@ tap.test('should route requests by hostname', async () => {
// Test handling of hostname with port number
tap.test('should handle hostname with port number', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
const config = createRouteConfig(TEST_DOMAIN);
router.updateRoutes([config]);
const req = createMockRequest(`${TEST_DOMAIN}:443`);
const result = router.routeReq(req);
@ -72,8 +79,8 @@ tap.test('should handle hostname with port number', async () => {
// Test case-insensitive hostname matching
tap.test('should perform case-insensitive hostname matching', async () => {
const config = createProxyConfig(TEST_DOMAIN.toLowerCase());
router.setNewProxyConfigs([config]);
const config = createRouteConfig(TEST_DOMAIN.toLowerCase());
router.updateRoutes([config]);
const req = createMockRequest(TEST_DOMAIN.toUpperCase());
const result = router.routeReq(req);
@ -84,8 +91,8 @@ tap.test('should perform case-insensitive hostname matching', async () => {
// Test handling of unmatched hostnames
tap.test('should return undefined for unmatched hostnames', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
const config = createRouteConfig(TEST_DOMAIN);
router.updateRoutes([config]);
const req = createMockRequest('unknown.domain.com');
const result = router.routeReq(req);
@ -95,18 +102,16 @@ tap.test('should return undefined for unmatched hostnames', async () => {
// Test adding path patterns
tap.test('should match requests using path patterns', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
// Add a path pattern to the config
router.setPathPattern(config, '/api/users');
const config = createRouteConfig(TEST_DOMAIN);
config.match.path = '/api/users';
router.updateRoutes([config]);
// Test that path matches
const req1 = createMockRequest(TEST_DOMAIN, '/api/users');
const result1 = router.routeReqWithDetails(req1);
expect(result1).toBeTruthy();
expect(result1.config).toEqual(config);
expect(result1.route).toEqual(config);
expect(result1.pathMatch).toEqual('/api/users');
// Test that non-matching path doesn't match
@ -118,17 +123,16 @@ tap.test('should match requests using path patterns', async () => {
// Test handling wildcard patterns
tap.test('should support wildcard path patterns', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
router.setPathPattern(config, '/api/*');
const config = createRouteConfig(TEST_DOMAIN);
config.match.path = '/api/*';
router.updateRoutes([config]);
// Test with path that matches the wildcard pattern
const req = createMockRequest(TEST_DOMAIN, '/api/users/123');
const result = router.routeReqWithDetails(req);
expect(result).toBeTruthy();
expect(result.config).toEqual(config);
expect(result.route).toEqual(config);
expect(result.pathMatch).toEqual('/api');
// Print the actual value to diagnose issues
@ -139,31 +143,31 @@ tap.test('should support wildcard path patterns', async () => {
// Test extracting path parameters
tap.test('should extract path parameters from URL', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
router.setPathPattern(config, '/users/:id/profile');
const config = createRouteConfig(TEST_DOMAIN);
config.match.path = '/users/:id/profile';
router.updateRoutes([config]);
const req = createMockRequest(TEST_DOMAIN, '/users/123/profile');
const result = router.routeReqWithDetails(req);
expect(result).toBeTruthy();
expect(result.config).toEqual(config);
expect(result.route).toEqual(config);
expect(result.pathParams).toBeTruthy();
expect(result.pathParams.id).toEqual('123');
});
// Test multiple configs for same hostname with different paths
tap.test('should support multiple configs for same hostname with different paths', async () => {
const apiConfig = createProxyConfig(TEST_DOMAIN, '10.0.0.1', 8001);
const webConfig = createProxyConfig(TEST_DOMAIN, '10.0.0.2', 8002);
const apiConfig = createRouteConfig(TEST_DOMAIN, '10.0.0.1', 8001);
apiConfig.match.path = '/api';
apiConfig.name = 'api-route';
const webConfig = createRouteConfig(TEST_DOMAIN, '10.0.0.2', 8002);
webConfig.match.path = '/web';
webConfig.name = 'web-route';
// Add both configs
router.setNewProxyConfigs([apiConfig, webConfig]);
// Set different path patterns
router.setPathPattern(apiConfig, '/api');
router.setPathPattern(webConfig, '/web');
router.updateRoutes([apiConfig, webConfig]);
// Test API path routes to API config
const apiReq = createMockRequest(TEST_DOMAIN, '/api/users');
@ -186,8 +190,8 @@ tap.test('should support multiple configs for same hostname with different paths
// Test wildcard subdomains
tap.test('should match wildcard subdomains', async () => {
const wildcardConfig = createProxyConfig(TEST_WILDCARD);
router.setNewProxyConfigs([wildcardConfig]);
const wildcardConfig = createRouteConfig(TEST_WILDCARD);
router.updateRoutes([wildcardConfig]);
// Test that subdomain.example.com matches *.example.com
const req = createMockRequest('subdomain.example.com');
@ -199,8 +203,8 @@ tap.test('should match wildcard subdomains', async () => {
// Test TLD wildcards (example.*)
tap.test('should match TLD wildcards', async () => {
const tldWildcardConfig = createProxyConfig('example.*');
router.setNewProxyConfigs([tldWildcardConfig]);
const tldWildcardConfig = createRouteConfig('example.*');
router.updateRoutes([tldWildcardConfig]);
// Test that example.com matches example.*
const req1 = createMockRequest('example.com');
@ -222,8 +226,8 @@ tap.test('should match TLD wildcards', async () => {
// Test complex pattern matching (*.lossless*)
tap.test('should match complex wildcard patterns', async () => {
const complexWildcardConfig = createProxyConfig('*.lossless*');
router.setNewProxyConfigs([complexWildcardConfig]);
const complexWildcardConfig = createRouteConfig('*.lossless*');
router.updateRoutes([complexWildcardConfig]);
// Test that sub.lossless.com matches *.lossless*
const req1 = createMockRequest('sub.lossless.com');
@ -245,10 +249,10 @@ tap.test('should match complex wildcard patterns', async () => {
// Test default configuration fallback
tap.test('should fall back to default configuration', async () => {
const defaultConfig = createProxyConfig('*');
const specificConfig = createProxyConfig(TEST_DOMAIN);
const defaultConfig = createRouteConfig('*');
const specificConfig = createRouteConfig(TEST_DOMAIN);
router.setNewProxyConfigs([defaultConfig, specificConfig]);
router.updateRoutes([defaultConfig, specificConfig]);
// Test specific domain routes to specific config
const specificReq = createMockRequest(TEST_DOMAIN);
@ -265,10 +269,10 @@ tap.test('should fall back to default configuration', async () => {
// Test priority between exact and wildcard matches
tap.test('should prioritize exact hostname over wildcard', async () => {
const wildcardConfig = createProxyConfig(TEST_WILDCARD);
const exactConfig = createProxyConfig(TEST_SUBDOMAIN);
const wildcardConfig = createRouteConfig(TEST_WILDCARD);
const exactConfig = createRouteConfig(TEST_SUBDOMAIN);
router.setNewProxyConfigs([wildcardConfig, exactConfig]);
router.updateRoutes([wildcardConfig, exactConfig]);
// Test that exact match takes priority
const req = createMockRequest(TEST_SUBDOMAIN);
@ -279,11 +283,11 @@ tap.test('should prioritize exact hostname over wildcard', async () => {
// Test adding and removing configurations
tap.test('should manage configurations correctly', async () => {
router.setNewProxyConfigs([]);
router.updateRoutes([]);
// Add a config
const config = createProxyConfig(TEST_DOMAIN);
router.addProxyConfig(config);
const config = createRouteConfig(TEST_DOMAIN);
router.updateRoutes([config]);
// Verify routing works
const req = createMockRequest(TEST_DOMAIN);
@ -292,8 +296,7 @@ tap.test('should manage configurations correctly', async () => {
expect(result).toEqual(config);
// Remove the config and verify it no longer routes
const removed = router.removeProxyConfig(TEST_DOMAIN);
expect(removed).toBeTrue();
router.updateRoutes([]);
result = router.routeReq(req);
expect(result).toBeUndefined();
@ -301,13 +304,16 @@ tap.test('should manage configurations correctly', async () => {
// Test path pattern specificity
tap.test('should prioritize more specific path patterns', async () => {
const genericConfig = createProxyConfig(TEST_DOMAIN, '10.0.0.1', 8001);
const specificConfig = createProxyConfig(TEST_DOMAIN, '10.0.0.2', 8002);
const genericConfig = createRouteConfig(TEST_DOMAIN, '10.0.0.1', 8001);
genericConfig.match.path = '/api/*';
genericConfig.name = 'generic-api';
router.setNewProxyConfigs([genericConfig, specificConfig]);
const specificConfig = createRouteConfig(TEST_DOMAIN, '10.0.0.2', 8002);
specificConfig.match.path = '/api/users';
specificConfig.name = 'specific-api';
specificConfig.priority = 10; // Higher priority
router.setPathPattern(genericConfig, '/api/*');
router.setPathPattern(specificConfig, '/api/users');
router.updateRoutes([genericConfig, specificConfig]);
// The more specific '/api/users' should match before the '/api/*' wildcard
const req = createMockRequest(TEST_DOMAIN, '/api/users');
@ -316,24 +322,29 @@ tap.test('should prioritize more specific path patterns', async () => {
expect(result).toEqual(specificConfig);
});
// Test getHostnames method
tap.test('should retrieve all configured hostnames', async () => {
router.setNewProxyConfigs([
createProxyConfig(TEST_DOMAIN),
createProxyConfig(TEST_SUBDOMAIN)
]);
// Test multiple hostnames
tap.test('should handle multiple configured hostnames', async () => {
const routes = [
createRouteConfig(TEST_DOMAIN),
createRouteConfig(TEST_SUBDOMAIN)
];
router.updateRoutes(routes);
const hostnames = router.getHostnames();
// Test first domain routes correctly
const req1 = createMockRequest(TEST_DOMAIN);
const result1 = router.routeReq(req1);
expect(result1).toEqual(routes[0]);
expect(hostnames.length).toEqual(2);
expect(hostnames).toContain(TEST_DOMAIN.toLowerCase());
expect(hostnames).toContain(TEST_SUBDOMAIN.toLowerCase());
// Test second domain routes correctly
const req2 = createMockRequest(TEST_SUBDOMAIN);
const result2 = router.routeReq(req2);
expect(result2).toEqual(routes[1]);
});
// Test handling missing host header
tap.test('should handle missing host header', async () => {
const defaultConfig = createProxyConfig('*');
router.setNewProxyConfigs([defaultConfig]);
const defaultConfig = createRouteConfig('*');
router.updateRoutes([defaultConfig]);
const req = createMockRequest('');
req.headers.host = undefined;
@ -345,16 +356,15 @@ tap.test('should handle missing host header', async () => {
// Test complex path parameters
tap.test('should handle complex path parameters', async () => {
const config = createProxyConfig(TEST_DOMAIN);
router.setNewProxyConfigs([config]);
router.setPathPattern(config, '/api/:version/users/:userId/posts/:postId');
const config = createRouteConfig(TEST_DOMAIN);
config.match.path = '/api/:version/users/:userId/posts/:postId';
router.updateRoutes([config]);
const req = createMockRequest(TEST_DOMAIN, '/api/v1/users/123/posts/456');
const result = router.routeReqWithDetails(req);
expect(result).toBeTruthy();
expect(result.config).toEqual(config);
expect(result.route).toEqual(config);
expect(result.pathParams).toBeTruthy();
expect(result.pathParams.version).toEqual('v1');
expect(result.pathParams.userId).toEqual('123');
@ -367,10 +377,10 @@ tap.test('should handle many configurations efficiently', async () => {
// Create many configs with different hostnames
for (let i = 0; i < 100; i++) {
configs.push(createProxyConfig(`host-${i}.example.com`));
configs.push(createRouteConfig(`host-${i}.example.com`));
}
router.setNewProxyConfigs(configs);
router.updateRoutes(configs);
// Test middle of the list to avoid best/worst case
const req = createMockRequest('host-50.example.com');
@ -382,11 +392,12 @@ tap.test('should handle many configurations efficiently', async () => {
// Test cleanup
tap.test('cleanup proxy router test environment', async () => {
// Clear all configurations
router.setNewProxyConfigs([]);
router.updateRoutes([]);
// Verify empty state
expect(router.getHostnames().length).toEqual(0);
expect(router.getProxyConfigs().length).toEqual(0);
// Verify empty state by testing that no routes match
const req = createMockRequest(TEST_DOMAIN);
const result = router.routeReq(req);
expect(result).toBeUndefined();
});
export default tap.start();