236 lines
7.9 KiB
TypeScript
236 lines
7.9 KiB
TypeScript
import { tap, expect } from '@push.rocks/tapbundle';
|
|
import * as plugins from '../ts/plugins.js';
|
|
|
|
// Import from individual modules to avoid naming conflicts
|
|
import {
|
|
// Route helpers
|
|
createHttpRoute,
|
|
createHttpsTerminateRoute,
|
|
createStaticFileRoute,
|
|
createApiRoute,
|
|
createWebSocketRoute
|
|
} from '../ts/proxies/smart-proxy/utils/route-helpers.js';
|
|
|
|
import {
|
|
// Route validators
|
|
validateRouteConfig,
|
|
validateRoutes,
|
|
isValidDomain,
|
|
isValidPort
|
|
} from '../ts/proxies/smart-proxy/utils/route-validators.js';
|
|
|
|
import {
|
|
// Route utilities
|
|
mergeRouteConfigs,
|
|
findMatchingRoutes,
|
|
routeMatchesDomain,
|
|
routeMatchesPort
|
|
} from '../ts/proxies/smart-proxy/utils/route-utils.js';
|
|
|
|
import {
|
|
// Route patterns
|
|
createApiGatewayRoute,
|
|
createStaticFileServerRoute,
|
|
createWebSocketRoute as createWebSocketPattern,
|
|
addRateLimiting
|
|
} from '../ts/proxies/smart-proxy/utils/route-patterns.js';
|
|
|
|
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
|
|
|
tap.test('Route Validation - isValidDomain', async () => {
|
|
// Valid domains
|
|
expect(isValidDomain('example.com')).toBeTrue();
|
|
expect(isValidDomain('sub.example.com')).toBeTrue();
|
|
expect(isValidDomain('*.example.com')).toBeTrue();
|
|
|
|
// Invalid domains
|
|
expect(isValidDomain('example')).toBeFalse();
|
|
expect(isValidDomain('example.')).toBeFalse();
|
|
expect(isValidDomain('example..com')).toBeFalse();
|
|
expect(isValidDomain('*.*.example.com')).toBeFalse();
|
|
expect(isValidDomain('-example.com')).toBeFalse();
|
|
});
|
|
|
|
tap.test('Route Validation - isValidPort', async () => {
|
|
// Valid ports
|
|
expect(isValidPort(80)).toBeTrue();
|
|
expect(isValidPort(443)).toBeTrue();
|
|
expect(isValidPort(8080)).toBeTrue();
|
|
expect(isValidPort([80, 443])).toBeTrue();
|
|
|
|
// Invalid ports
|
|
expect(isValidPort(0)).toBeFalse();
|
|
expect(isValidPort(65536)).toBeFalse();
|
|
expect(isValidPort(-1)).toBeFalse();
|
|
expect(isValidPort([0, 80])).toBeFalse();
|
|
});
|
|
|
|
tap.test('Route Validation - validateRouteConfig', async () => {
|
|
// Valid route config
|
|
const validRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
|
|
const validResult = validateRouteConfig(validRoute);
|
|
expect(validResult.valid).toBeTrue();
|
|
expect(validResult.errors.length).toEqual(0);
|
|
|
|
// Invalid route config (missing target)
|
|
const invalidRoute: IRouteConfig = {
|
|
match: {
|
|
domains: 'example.com',
|
|
ports: 80
|
|
},
|
|
action: {
|
|
type: 'forward'
|
|
},
|
|
name: 'Invalid Route'
|
|
};
|
|
const invalidResult = validateRouteConfig(invalidRoute);
|
|
expect(invalidResult.valid).toBeFalse();
|
|
expect(invalidResult.errors.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('Route Utilities - mergeRouteConfigs', async () => {
|
|
// Base route
|
|
const baseRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
|
|
|
|
// Override with different name and port
|
|
const overrideRoute: Partial<IRouteConfig> = {
|
|
name: 'Merged Route',
|
|
match: {
|
|
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');
|
|
});
|
|
|
|
tap.test('Route Matching - routeMatchesDomain', async () => {
|
|
// Create route with wildcard domain
|
|
const wildcardRoute = createHttpRoute('*.example.com', { host: 'localhost', port: 3000 });
|
|
|
|
// Create route with exact domain
|
|
const exactRoute = createHttpRoute('example.com', { host: 'localhost', port: 3000 });
|
|
|
|
// 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();
|
|
});
|
|
|
|
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 })
|
|
];
|
|
|
|
// 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');
|
|
});
|
|
|
|
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.target.port).toEqual(3000);
|
|
expect(apiGatewayRoute.action.tls?.mode).toEqual('terminate');
|
|
|
|
// Check if CORS headers are added
|
|
const result = validateRouteConfig(apiGatewayRoute);
|
|
expect(result.valid).toBeTrue();
|
|
});
|
|
|
|
tap.test('Route Patterns - createStaticFileServerRoute', async () => {
|
|
// Create static file server route
|
|
const staticRoute = createStaticFileServerRoute(
|
|
'static.example.com',
|
|
'/var/www/html',
|
|
{
|
|
useTls: true,
|
|
cacheControl: 'public, max-age=7200'
|
|
}
|
|
);
|
|
|
|
// Validate route configuration
|
|
expect(staticRoute.match.domains).toEqual('static.example.com');
|
|
expect(staticRoute.action.type).toEqual('static');
|
|
expect(staticRoute.action.static?.root).toEqual('/var/www/html');
|
|
expect(staticRoute.action.static?.headers?.['Cache-Control']).toEqual('public, max-age=7200');
|
|
|
|
const result = validateRouteConfig(staticRoute);
|
|
expect(result.valid).toBeTrue();
|
|
});
|
|
|
|
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 (security property may be undefined if not implemented yet)
|
|
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();
|
|
});
|
|
|
|
export default tap.start(); |