BREAKING CHANGE(smart-proxy): remove route helper APIs and standardize route configuration on plain route objects

This commit is contained in:
2026-03-26 20:45:41 +00:00
parent 47140e5403
commit 788ccea81e
32 changed files with 1159 additions and 2840 deletions

View File

@@ -1,21 +1,7 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../ts/plugins.js';
// Import from individual modules to avoid naming conflicts
import {
// Route helpers
createHttpRoute,
createHttpsTerminateRoute,
createApiRoute,
createWebSocketRoute,
createHttpToHttpsRedirect,
createHttpsPassthroughRoute,
createCompleteHttpsServer,
createLoadBalancerRoute
} from '../ts/proxies/smart-proxy/utils/route-helpers.js';
import {
// Route validators
validateRouteConfig,
validateRoutes,
isValidDomain,
@@ -27,7 +13,6 @@ import {
} from '../ts/proxies/smart-proxy/utils/route-validator.js';
import {
// Route utilities
mergeRouteConfigs,
findMatchingRoutes,
findBestMatchingRoute,
@@ -39,16 +24,6 @@ import {
cloneRoute
} from '../ts/proxies/smart-proxy/utils/route-utils.js';
import {
// Route patterns
createApiGatewayRoute,
createWebSocketRoute as createWebSocketPattern,
createLoadBalancerRoute as createLbPattern,
addRateLimiting,
addBasicAuth,
addJwtAuth
} from '../ts/proxies/smart-proxy/utils/route-helpers.js';
import type {
IRouteConfig,
IRouteMatch,
@@ -84,7 +59,7 @@ tap.test('Route Validation - isValidPort', async () => {
expect(isValidPort(443)).toBeTrue();
expect(isValidPort(8080)).toBeTrue();
expect(isValidPort([80, 443])).toBeTrue();
// Invalid ports
expect(isValidPort(0)).toBeFalse();
expect(isValidPort(65536)).toBeFalse();
@@ -101,7 +76,7 @@ tap.test('Route Validation - validateRouteMatch', async () => {
const validResult = validateRouteMatch(validMatch);
expect(validResult.valid).toBeTrue();
expect(validResult.errors.length).toEqual(0);
// Invalid match configuration (invalid domain)
const invalidMatch: IRouteMatch = {
ports: 80,
@@ -111,7 +86,7 @@ tap.test('Route Validation - validateRouteMatch', async () => {
expect(invalidResult.valid).toBeFalse();
expect(invalidResult.errors.length).toBeGreaterThan(0);
expect(invalidResult.errors[0]).toInclude('Invalid domain');
// Invalid match configuration (invalid port)
const invalidPortMatch: IRouteMatch = {
ports: 0,
@@ -121,7 +96,7 @@ tap.test('Route Validation - validateRouteMatch', async () => {
expect(invalidPortResult.valid).toBeFalse();
expect(invalidPortResult.errors.length).toBeGreaterThan(0);
expect(invalidPortResult.errors[0]).toInclude('Invalid port');
// Test path validation
const invalidPathMatch: IRouteMatch = {
ports: 80,
@@ -146,7 +121,7 @@ tap.test('Route Validation - validateRouteAction', async () => {
const validForwardResult = validateRouteAction(validForwardAction);
expect(validForwardResult.valid).toBeTrue();
expect(validForwardResult.errors.length).toEqual(0);
// Valid socket-handler action
const validSocketAction: IRouteAction = {
type: 'socket-handler',
@@ -157,7 +132,7 @@ tap.test('Route Validation - validateRouteAction', async () => {
const validSocketResult = validateRouteAction(validSocketAction);
expect(validSocketResult.valid).toBeTrue();
expect(validSocketResult.errors.length).toEqual(0);
// Invalid action (missing targets)
const invalidAction: IRouteAction = {
type: 'forward'
@@ -166,7 +141,7 @@ tap.test('Route Validation - validateRouteAction', async () => {
expect(invalidResult.valid).toBeFalse();
expect(invalidResult.errors.length).toBeGreaterThan(0);
expect(invalidResult.errors[0]).toInclude('Targets array is required');
// Invalid action (missing socket handler)
const invalidSocketAction: IRouteAction = {
type: 'socket-handler'
@@ -179,11 +154,15 @@ tap.test('Route Validation - validateRouteAction', async () => {
tap.test('Route Validation - validateRouteConfig', async () => {
// Valid route config
const validRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const validRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
const validResult = validateRouteConfig(validRoute);
expect(validResult.valid).toBeTrue();
expect(validResult.errors.length).toEqual(0);
// Invalid route config (missing targets)
const invalidRoute: IRouteConfig = {
match: {
@@ -203,7 +182,11 @@ tap.test('Route Validation - validateRouteConfig', async () => {
tap.test('Route Validation - validateRoutes', async () => {
// Create valid and invalid routes
const routes = [
createHttpRoute('example.com', { host: 'localhost', port: 3000 }),
{
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
} as IRouteConfig,
{
match: {
domains: 'invalid..domain',
@@ -217,9 +200,13 @@ tap.test('Route Validation - validateRoutes', async () => {
}
}
} as IRouteConfig,
createHttpsTerminateRoute('secure.example.com', { host: 'localhost', port: 3001 })
{
match: { ports: 443, domains: 'secure.example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3001 }], tls: { mode: 'terminate', certificate: 'auto' } },
name: 'HTTPS Terminate Route for secure.example.com',
} as IRouteConfig
];
const result = validateRoutes(routes);
expect(result.valid).toBeFalse();
expect(result.errors.length).toEqual(1);
@@ -230,13 +217,13 @@ tap.test('Route Validation - validateRoutes', async () => {
tap.test('Route Validation - hasRequiredPropertiesForAction', async () => {
// Forward action
const forwardRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const forwardRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
expect(hasRequiredPropertiesForAction(forwardRoute, 'forward')).toBeTrue();
// Socket handler action (redirect functionality)
const redirectRoute = createHttpToHttpsRedirect('example.com');
expect(hasRequiredPropertiesForAction(redirectRoute, 'socket-handler')).toBeTrue();
// Socket handler action
const socketRoute: IRouteConfig = {
match: {
@@ -252,7 +239,7 @@ tap.test('Route Validation - hasRequiredPropertiesForAction', async () => {
name: 'Socket Handler Route'
};
expect(hasRequiredPropertiesForAction(socketRoute, 'socket-handler')).toBeTrue();
// Missing required properties
const invalidForwardRoute: IRouteConfig = {
match: {
@@ -269,9 +256,13 @@ tap.test('Route Validation - hasRequiredPropertiesForAction', async () => {
tap.test('Route Validation - assertValidRoute', async () => {
// Valid route
const validRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const validRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
expect(() => assertValidRoute(validRoute)).not.toThrow();
// Invalid route
const invalidRoute: IRouteConfig = {
match: {
@@ -290,8 +281,12 @@ tap.test('Route Validation - assertValidRoute', async () => {
tap.test('Route Utilities - mergeRouteConfigs', async () => {
// Base route
const baseRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const baseRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
// Override with different name and port
const overrideRoute: Partial<IRouteConfig> = {
name: 'Merged Route',
@@ -299,16 +294,16 @@ tap.test('Route Utilities - mergeRouteConfigs', async () => {
ports: 8080
}
};
// Merge configs
const mergedRoute = mergeRouteConfigs(baseRoute, overrideRoute);
// Check merged properties
expect(mergedRoute.name).toEqual('Merged Route');
expect(mergedRoute.match.ports).toEqual(8080);
expect(mergedRoute.match.domains).toEqual('example.com');
expect(mergedRoute.action.type).toEqual('forward');
// Test merging action properties
const actionOverride: Partial<IRouteConfig> = {
action: {
@@ -319,11 +314,11 @@ tap.test('Route Utilities - mergeRouteConfigs', async () => {
}]
}
};
const actionMergedRoute = mergeRouteConfigs(baseRoute, actionOverride);
expect(actionMergedRoute.action.targets?.[0]?.host).toEqual('new-host.local');
expect(actionMergedRoute.action.targets?.[0]?.port).toEqual(5000);
// Test replacing action with socket handler
const typeChangeOverride: Partial<IRouteConfig> = {
action: {
@@ -336,7 +331,7 @@ tap.test('Route Utilities - mergeRouteConfigs', async () => {
}
}
};
const typeChangedRoute = mergeRouteConfigs(baseRoute, typeChangeOverride);
expect(typeChangedRoute.action.type).toEqual('socket-handler');
expect(typeChangedRoute.action.socketHandler).toBeDefined();
@@ -345,37 +340,53 @@ tap.test('Route Utilities - mergeRouteConfigs', async () => {
tap.test('Route Matching - routeMatchesDomain', async () => {
// Create route with wildcard domain
const wildcardRoute = createHttpRoute('*.example.com', { host: 'localhost', port: 3000 });
const wildcardRoute: IRouteConfig = {
match: { ports: 80, domains: '*.example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for *.example.com',
};
// Create route with exact domain
const exactRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const exactRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
// Create route with multiple domains
const multiDomainRoute = createHttpRoute(['example.com', 'example.org'], { host: 'localhost', port: 3000 });
const multiDomainRoute: IRouteConfig = {
match: { ports: 80, domains: ['example.com', 'example.org'] },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com,example.org',
};
// Test wildcard domain matching
expect(routeMatchesDomain(wildcardRoute, 'sub.example.com')).toBeTrue();
expect(routeMatchesDomain(wildcardRoute, 'another.example.com')).toBeTrue();
expect(routeMatchesDomain(wildcardRoute, 'example.com')).toBeFalse();
expect(routeMatchesDomain(wildcardRoute, 'example.org')).toBeFalse();
// Test exact domain matching
expect(routeMatchesDomain(exactRoute, 'example.com')).toBeTrue();
expect(routeMatchesDomain(exactRoute, 'sub.example.com')).toBeFalse();
// Test multiple domains matching
expect(routeMatchesDomain(multiDomainRoute, 'example.com')).toBeTrue();
expect(routeMatchesDomain(multiDomainRoute, 'example.org')).toBeTrue();
expect(routeMatchesDomain(multiDomainRoute, 'example.net')).toBeFalse();
// Test case insensitivity
expect(routeMatchesDomain(exactRoute, 'Example.Com')).toBeTrue();
});
tap.test('Route Matching - routeMatchesPort', async () => {
// Create routes with different port configurations
const singlePortRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const singlePortRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
const multiPortRoute: IRouteConfig = {
match: {
domains: 'example.com',
@@ -389,7 +400,7 @@ tap.test('Route Matching - routeMatchesPort', async () => {
}]
}
};
const portRangeRoute: IRouteConfig = {
match: {
domains: 'example.com',
@@ -403,16 +414,16 @@ tap.test('Route Matching - routeMatchesPort', async () => {
}]
}
};
// Test single port matching
expect(routeMatchesPort(singlePortRoute, 80)).toBeTrue();
expect(routeMatchesPort(singlePortRoute, 443)).toBeFalse();
// Test multi-port matching
expect(routeMatchesPort(multiPortRoute, 80)).toBeTrue();
expect(routeMatchesPort(multiPortRoute, 8080)).toBeTrue();
expect(routeMatchesPort(multiPortRoute, 3000)).toBeFalse();
// Test port range matching
expect(routeMatchesPort(portRangeRoute, 8000)).toBeTrue();
expect(routeMatchesPort(portRangeRoute, 8500)).toBeTrue();
@@ -437,11 +448,11 @@ tap.test('Route Matching - routeMatchesPath', async () => {
}]
}
};
// Test prefix matching with wildcard (not trailing slash)
const prefixPathRoute: IRouteConfig = {
match: {
domains: 'example.com',
domains: 'example.com',
ports: 80,
path: '/api/*'
},
@@ -453,7 +464,7 @@ tap.test('Route Matching - routeMatchesPath', async () => {
}]
}
};
const wildcardPathRoute: IRouteConfig = {
match: {
domains: 'example.com',
@@ -468,17 +479,17 @@ tap.test('Route Matching - routeMatchesPath', async () => {
}]
}
};
// Test exact path matching
expect(routeMatchesPath(exactPathRoute, '/api')).toBeTrue();
expect(routeMatchesPath(exactPathRoute, '/api/users')).toBeFalse();
expect(routeMatchesPath(exactPathRoute, '/app')).toBeFalse();
// Test prefix path matching with wildcard
expect(routeMatchesPath(prefixPathRoute, '/api/')).toBeFalse(); // Wildcard requires content after /api/
expect(routeMatchesPath(prefixPathRoute, '/api/users')).toBeTrue();
expect(routeMatchesPath(prefixPathRoute, '/app/')).toBeFalse();
// Test wildcard path matching
expect(routeMatchesPath(wildcardPathRoute, '/api/users')).toBeTrue();
expect(routeMatchesPath(wildcardPathRoute, '/api/products')).toBeTrue();
@@ -504,30 +515,34 @@ tap.test('Route Matching - routeMatchesHeaders', async () => {
}]
}
};
// Test header matching
expect(routeMatchesHeaders(headerRoute, {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
})).toBeTrue();
expect(routeMatchesHeaders(headerRoute, {
'Content-Type': 'application/json',
'X-Custom-Header': 'value',
'Extra-Header': 'something'
})).toBeTrue();
expect(routeMatchesHeaders(headerRoute, {
'Content-Type': 'application/json'
})).toBeFalse();
expect(routeMatchesHeaders(headerRoute, {
'Content-Type': 'text/html',
'X-Custom-Header': 'value'
})).toBeFalse();
// Route without header matching should match any headers
const noHeaderRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const noHeaderRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
expect(routeMatchesHeaders(noHeaderRoute, {
'Content-Type': 'application/json'
})).toBeTrue();
@@ -536,78 +551,118 @@ tap.test('Route Matching - routeMatchesHeaders', async () => {
tap.test('Route Finding - findMatchingRoutes', async () => {
// Create multiple routes
const routes: IRouteConfig[] = [
createHttpRoute('example.com', { host: 'localhost', port: 3000 }),
createHttpsTerminateRoute('secure.example.com', { host: 'localhost', port: 3001 }),
createApiRoute('api.example.com', '/v1', { host: 'localhost', port: 3002 }),
createWebSocketRoute('ws.example.com', '/socket', { host: 'localhost', port: 3003 })
{
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
},
{
match: { ports: 443, domains: 'secure.example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3001 }], tls: { mode: 'terminate', certificate: 'auto' } },
name: 'HTTPS Route for secure.example.com',
},
{
match: { ports: 443, domains: 'api.example.com', path: '/v1/*' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3002 }], tls: { mode: 'terminate', certificate: 'auto' } },
name: 'API Route for api.example.com',
},
{
match: { ports: 443, domains: 'ws.example.com', path: '/socket' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3003 }], tls: { mode: 'terminate', certificate: 'auto' }, websocket: { enabled: true } },
name: 'WebSocket Route for ws.example.com',
},
];
// Set priorities
routes[0].priority = 10;
routes[1].priority = 20;
routes[2].priority = 30;
routes[3].priority = 40;
// Find routes for different criteria
const httpMatches = findMatchingRoutes(routes, { domain: 'example.com', port: 80 });
expect(httpMatches.length).toEqual(1);
expect(httpMatches[0].name).toInclude('HTTP Route');
const httpsMatches = findMatchingRoutes(routes, { domain: 'secure.example.com', port: 443 });
expect(httpsMatches.length).toEqual(1);
expect(httpsMatches[0].name).toInclude('HTTPS Route');
const apiMatches = findMatchingRoutes(routes, { domain: 'api.example.com', path: '/v1/users' });
expect(apiMatches.length).toEqual(1);
expect(apiMatches[0].name).toInclude('API Route');
const wsMatches = findMatchingRoutes(routes, { domain: 'ws.example.com', path: '/socket' });
expect(wsMatches.length).toEqual(1);
expect(wsMatches[0].name).toInclude('WebSocket Route');
// Test finding multiple routes that match same criteria
const route1 = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const route1: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
route1.priority = 10;
const route2 = createHttpRoute('example.com', { host: 'localhost', port: 3001 });
const route2: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3001 }] },
name: 'HTTP Route for example.com',
};
route2.priority = 20;
route2.match.path = '/api';
const multiMatchRoutes = [route1, route2];
const multiMatches = findMatchingRoutes(multiMatchRoutes, { domain: 'example.com', port: 80 });
expect(multiMatches.length).toEqual(2);
expect(multiMatches[0].priority).toEqual(20); // Higher priority should be first
expect(multiMatches[1].priority).toEqual(10);
// Test disabled routes
const disabledRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const disabledRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
disabledRoute.enabled = false;
const enabledRoutes = findMatchingRoutes([disabledRoute], { domain: 'example.com', port: 80 });
expect(enabledRoutes.length).toEqual(0);
});
tap.test('Route Finding - findBestMatchingRoute', async () => {
// Create multiple routes with different priorities
const route1 = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const route1: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
route1.priority = 10;
const route2 = createHttpRoute('example.com', { host: 'localhost', port: 3001 });
const route2: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3001 }] },
name: 'HTTP Route for example.com',
};
route2.priority = 20;
route2.match.path = '/api';
const route3 = createHttpRoute('example.com', { host: 'localhost', port: 3002 });
const route3: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3002 }] },
name: 'HTTP Route for example.com',
};
route3.priority = 30;
route3.match.path = '/api/users';
const routes = [route1, route2, route3];
// Find best route for different criteria
const bestGeneral = findBestMatchingRoute(routes, { domain: 'example.com', port: 80 });
expect(bestGeneral).not.toBeUndefined();
expect(bestGeneral?.priority).toEqual(30);
// Test when no routes match
const noMatch = findBestMatchingRoute(routes, { domain: 'unknown.com', port: 80 });
expect(noMatch).toBeUndefined();
@@ -615,389 +670,54 @@ tap.test('Route Finding - findBestMatchingRoute', async () => {
tap.test('Route Utilities - generateRouteId', async () => {
// Test ID generation for different route types
const httpRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
const httpRoute: IRouteConfig = {
match: { ports: 80, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com',
};
const httpId = generateRouteId(httpRoute);
expect(httpId).toInclude('example-com');
expect(httpId).toInclude('80');
expect(httpId).toInclude('forward');
const httpsRoute = createHttpsTerminateRoute('secure.example.com', { host: 'localhost', port: 3001 });
const httpsRoute: IRouteConfig = {
match: { ports: 443, domains: 'secure.example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3001 }], tls: { mode: 'terminate', certificate: 'auto' } },
name: 'HTTPS Terminate Route for secure.example.com',
};
const httpsId = generateRouteId(httpsRoute);
expect(httpsId).toInclude('secure-example-com');
expect(httpsId).toInclude('443');
expect(httpsId).toInclude('forward');
const multiDomainRoute = createHttpRoute(['example.com', 'example.org'], { host: 'localhost', port: 3000 });
const multiDomainRoute: IRouteConfig = {
match: { ports: 80, domains: ['example.com', 'example.org'] },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }] },
name: 'HTTP Route for example.com,example.org',
};
const multiDomainId = generateRouteId(multiDomainRoute);
expect(multiDomainId).toInclude('example-com-example-org');
});
tap.test('Route Utilities - cloneRoute', async () => {
// Create a route and clone it
const originalRoute = createHttpsTerminateRoute('example.com', { host: 'localhost', port: 3000 }, {
certificate: 'auto',
name: 'Original Route'
});
const originalRoute: IRouteConfig = {
match: { ports: 443, domains: 'example.com' },
action: { type: 'forward', targets: [{ host: 'localhost', port: 3000 }], tls: { mode: 'terminate', certificate: 'auto' } },
name: 'Original Route',
};
const clonedRoute = cloneRoute(originalRoute);
// Check that the values are identical
expect(clonedRoute.name).toEqual(originalRoute.name);
expect(clonedRoute.match.domains).toEqual(originalRoute.match.domains);
expect(clonedRoute.action.type).toEqual(originalRoute.action.type);
expect(clonedRoute.action.targets?.[0]?.port).toEqual(originalRoute.action.targets?.[0]?.port);
// Modify the clone and check that the original is unchanged
clonedRoute.name = 'Modified Clone';
expect(originalRoute.name).toEqual('Original Route');
});
// --------------------------------- Route Helper Tests ---------------------------------
tap.test('Route Helpers - createHttpRoute', async () => {
const route = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
expect(route.match.domains).toEqual('example.com');
expect(route.match.ports).toEqual(80);
expect(route.action.type).toEqual('forward');
expect(route.action.targets?.[0]?.host).toEqual('localhost');
expect(route.action.targets?.[0]?.port).toEqual(3000);
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createHttpsTerminateRoute', async () => {
const route = createHttpsTerminateRoute('example.com', { host: 'localhost', port: 3000 }, {
certificate: 'auto'
});
expect(route.match.domains).toEqual('example.com');
expect(route.match.ports).toEqual(443);
expect(route.action.type).toEqual('forward');
expect(route.action.tls.mode).toEqual('terminate');
expect(route.action.tls.certificate).toEqual('auto');
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createHttpToHttpsRedirect', async () => {
const route = createHttpToHttpsRedirect('example.com');
expect(route.match.domains).toEqual('example.com');
expect(route.match.ports).toEqual(80);
expect(route.action.type).toEqual('socket-handler');
expect(route.action.socketHandler).toBeDefined();
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createHttpsPassthroughRoute', async () => {
const route = createHttpsPassthroughRoute('example.com', { host: 'localhost', port: 3000 });
expect(route.match.domains).toEqual('example.com');
expect(route.match.ports).toEqual(443);
expect(route.action.type).toEqual('forward');
expect(route.action.tls.mode).toEqual('passthrough');
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createCompleteHttpsServer', async () => {
const routes = createCompleteHttpsServer('example.com', { host: 'localhost', port: 3000 }, {
certificate: 'auto'
});
expect(routes.length).toEqual(2);
// HTTPS route
expect(routes[0].match.domains).toEqual('example.com');
expect(routes[0].match.ports).toEqual(443);
expect(routes[0].action.type).toEqual('forward');
expect(routes[0].action.tls.mode).toEqual('terminate');
// HTTP redirect route
expect(routes[1].match.domains).toEqual('example.com');
expect(routes[1].match.ports).toEqual(80);
expect(routes[1].action.type).toEqual('socket-handler');
const validation1 = validateRouteConfig(routes[0]);
const validation2 = validateRouteConfig(routes[1]);
expect(validation1.valid).toBeTrue();
expect(validation2.valid).toBeTrue();
});
// createStaticFileRoute has been removed - static file serving should be handled by
// external servers (nginx/apache) behind the proxy
tap.test('Route Helpers - createApiRoute', async () => {
const route = createApiRoute('api.example.com', '/v1', { host: 'localhost', port: 3000 }, {
useTls: true,
certificate: 'auto',
addCorsHeaders: true
});
expect(route.match.domains).toEqual('api.example.com');
expect(route.match.ports).toEqual(443);
expect(route.match.path).toEqual('/v1/*');
expect(route.action.type).toEqual('forward');
expect(route.action.tls.mode).toEqual('terminate');
// Check CORS headers if they exist
if (route.headers && route.headers.response) {
expect(route.headers.response['Access-Control-Allow-Origin']).toEqual('*');
}
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createWebSocketRoute', async () => {
const route = createWebSocketRoute('ws.example.com', '/socket', { host: 'localhost', port: 3000 }, {
useTls: true,
certificate: 'auto',
pingInterval: 15000
});
expect(route.match.domains).toEqual('ws.example.com');
expect(route.match.ports).toEqual(443);
expect(route.match.path).toEqual('/socket');
expect(route.action.type).toEqual('forward');
expect(route.action.tls.mode).toEqual('terminate');
// Check websocket configuration if it exists
if (route.action.websocket) {
expect(route.action.websocket.enabled).toBeTrue();
expect(route.action.websocket.pingInterval).toEqual(15000);
}
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
tap.test('Route Helpers - createLoadBalancerRoute', async () => {
const route = createLoadBalancerRoute(
'loadbalancer.example.com',
['server1.local', 'server2.local', 'server3.local'],
8080,
{
tls: {
mode: 'terminate',
certificate: 'auto'
}
}
);
expect(route.match.domains).toEqual('loadbalancer.example.com');
expect(route.match.ports).toEqual(443);
expect(route.action.type).toEqual('forward');
expect(route.action.targets).toBeDefined();
if (route.action.targets && Array.isArray(route.action.targets[0]?.host)) {
expect((route.action.targets[0].host as string[]).length).toEqual(3);
}
expect(route.action.targets?.[0]?.port).toEqual(8080);
expect(route.action.tls.mode).toEqual('terminate');
const validationResult = validateRouteConfig(route);
expect(validationResult.valid).toBeTrue();
});
// --------------------------------- Route Pattern Tests ---------------------------------
tap.test('Route Patterns - createApiGatewayRoute', async () => {
// Create API Gateway route
const apiGatewayRoute = createApiGatewayRoute(
'api.example.com',
'/v1',
{ host: 'localhost', port: 3000 },
{
useTls: true,
addCorsHeaders: true
}
);
// Validate route configuration
expect(apiGatewayRoute.match.domains).toEqual('api.example.com');
expect(apiGatewayRoute.match.path).toInclude('/v1');
expect(apiGatewayRoute.action.type).toEqual('forward');
expect(apiGatewayRoute.action.targets?.[0]?.port).toEqual(3000);
// Check TLS configuration
if (apiGatewayRoute.action.tls) {
expect(apiGatewayRoute.action.tls.mode).toEqual('terminate');
}
// Check CORS headers
if (apiGatewayRoute.headers && apiGatewayRoute.headers.response) {
expect(apiGatewayRoute.headers.response['Access-Control-Allow-Origin']).toEqual('*');
}
const result = validateRouteConfig(apiGatewayRoute);
expect(result.valid).toBeTrue();
});
// createStaticFileServerRoute has been removed - static file serving should be handled by
// external servers (nginx/apache) behind the proxy
tap.test('Route Patterns - createWebSocketPattern', async () => {
// Create WebSocket route pattern
const wsRoute = createWebSocketPattern(
'ws.example.com',
{ host: 'localhost', port: 3000 },
{
useTls: true,
path: '/socket',
pingInterval: 10000
}
);
// Validate route configuration
expect(wsRoute.match.domains).toEqual('ws.example.com');
expect(wsRoute.match.path).toEqual('/socket');
expect(wsRoute.action.type).toEqual('forward');
expect(wsRoute.action.targets?.[0]?.port).toEqual(3000);
// Check TLS configuration
if (wsRoute.action.tls) {
expect(wsRoute.action.tls.mode).toEqual('terminate');
}
// Check websocket configuration if it exists
if (wsRoute.action.websocket) {
expect(wsRoute.action.websocket.enabled).toBeTrue();
expect(wsRoute.action.websocket.pingInterval).toEqual(10000);
}
const result = validateRouteConfig(wsRoute);
expect(result.valid).toBeTrue();
});
tap.test('Route Patterns - createLoadBalancerRoute pattern', async () => {
// Create load balancer route pattern with missing algorithm as it might not be implemented yet
try {
const lbRoute = createLbPattern(
'lb.example.com',
[
{ host: 'server1.local', port: 8080 },
{ host: 'server2.local', port: 8080 },
{ host: 'server3.local', port: 8080 }
],
{
useTls: true
}
);
// Validate route configuration
expect(lbRoute.match.domains).toEqual('lb.example.com');
expect(lbRoute.action.type).toEqual('forward');
// Check target hosts
if (lbRoute.action.targets && Array.isArray(lbRoute.action.targets[0]?.host)) {
expect((lbRoute.action.targets[0].host as string[]).length).toEqual(3);
}
// Check TLS configuration
if (lbRoute.action.tls) {
expect(lbRoute.action.tls.mode).toEqual('terminate');
}
const result = validateRouteConfig(lbRoute);
expect(result.valid).toBeTrue();
} catch (error) {
// If the pattern is not implemented yet, skip this test
console.log('Load balancer pattern might not be fully implemented yet');
}
});
tap.test('Route Security - addRateLimiting', async () => {
// Create base route
const baseRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
// Add rate limiting
const secureRoute = addRateLimiting(baseRoute, {
maxRequests: 100,
window: 60, // 1 minute
keyBy: 'ip'
});
// Check if rate limiting is applied
if (secureRoute.security) {
expect(secureRoute.security.rateLimit?.enabled).toBeTrue();
expect(secureRoute.security.rateLimit?.maxRequests).toEqual(100);
expect(secureRoute.security.rateLimit?.window).toEqual(60);
expect(secureRoute.security.rateLimit?.keyBy).toEqual('ip');
} else {
// Skip this test if security features are not implemented yet
console.log('Security features not implemented yet in route configuration');
}
// Just check that the route itself is valid
const result = validateRouteConfig(secureRoute);
expect(result.valid).toBeTrue();
});
tap.test('Route Security - addBasicAuth', async () => {
// Create base route
const baseRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
// Add basic authentication
const authRoute = addBasicAuth(baseRoute, {
users: [
{ username: 'admin', password: 'secret' },
{ username: 'user', password: 'password' }
],
realm: 'Protected Area',
excludePaths: ['/public']
});
// Check if basic auth is applied
if (authRoute.security) {
expect(authRoute.security.basicAuth?.enabled).toBeTrue();
expect(authRoute.security.basicAuth?.users.length).toEqual(2);
expect(authRoute.security.basicAuth?.realm).toEqual('Protected Area');
expect(authRoute.security.basicAuth?.excludePaths).toInclude('/public');
} else {
// Skip this test if security features are not implemented yet
console.log('Security features not implemented yet in route configuration');
}
// Check that the route itself is valid
const result = validateRouteConfig(authRoute);
expect(result.valid).toBeTrue();
});
tap.test('Route Security - addJwtAuth', async () => {
// Create base route
const baseRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
// Add JWT authentication
const jwtRoute = addJwtAuth(baseRoute, {
secret: 'your-jwt-secret-key',
algorithm: 'HS256',
issuer: 'auth.example.com',
audience: 'api.example.com',
expiresIn: 3600
});
// Check if JWT auth is applied
if (jwtRoute.security) {
expect(jwtRoute.security.jwtAuth?.enabled).toBeTrue();
expect(jwtRoute.security.jwtAuth?.secret).toEqual('your-jwt-secret-key');
expect(jwtRoute.security.jwtAuth?.algorithm).toEqual('HS256');
expect(jwtRoute.security.jwtAuth?.issuer).toEqual('auth.example.com');
expect(jwtRoute.security.jwtAuth?.audience).toEqual('api.example.com');
expect(jwtRoute.security.jwtAuth?.expiresIn).toEqual(3600);
} else {
// Skip this test if security features are not implemented yet
console.log('Security features not implemented yet in route configuration');
}
// Check that the route itself is valid
const result = validateRouteConfig(jwtRoute);
expect(result.valid).toBeTrue();
});
export default tap.start();
export default tap.start();