fix(strcuture): refactor responsibilities
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartProxy } from '../ts/index.js';
|
||||
import * as plugins from '../ts/plugins.js';
|
||||
|
||||
/**
|
||||
* Test that verifies ACME challenge routes are properly created
|
||||
@ -22,22 +23,23 @@ tap.test('should create ACME challenge route with high ports', async (tools) =>
|
||||
target: { host: 'localhost', port: 8080 },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
certificate: 'auto' as const
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
acme: {
|
||||
email: 'test@test.local',
|
||||
port: 18080 // High port for ACME challenges
|
||||
email: 'test@example.com',
|
||||
port: 18080, // High port for ACME challenges
|
||||
useProduction: false // Use staging environment
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
|
||||
// Capture route updates
|
||||
const originalUpdateRoutes = (proxy as any).updateRoutesInternal.bind(proxy);
|
||||
(proxy as any).updateRoutesInternal = async function(routes: any[]) {
|
||||
const originalUpdateRoutes = (proxy as any).updateRoutes.bind(proxy);
|
||||
(proxy as any).updateRoutes = async function(routes: any[]) {
|
||||
capturedRoutes.push([...routes]);
|
||||
return originalUpdateRoutes(routes);
|
||||
};
|
||||
|
@ -19,20 +19,20 @@ tap.test('AcmeStateManager should track challenge routes correctly', async (tool
|
||||
};
|
||||
|
||||
// Initially no challenge routes
|
||||
tools.expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
tools.expect(stateManager.getActiveChallengeRoutes()).toHaveLength(0);
|
||||
expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
expect(stateManager.getActiveChallengeRoutes()).toEqual([]);
|
||||
|
||||
// Add challenge route
|
||||
stateManager.addChallengeRoute(challengeRoute);
|
||||
tools.expect(stateManager.isChallengeRouteActive()).toBeTrue();
|
||||
tools.expect(stateManager.getActiveChallengeRoutes()).toHaveLength(1);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()).toEqual(challengeRoute);
|
||||
expect(stateManager.isChallengeRouteActive()).toBeTrue();
|
||||
expect(stateManager.getActiveChallengeRoutes()).toHaveProperty("length", 1);
|
||||
expect(stateManager.getPrimaryChallengeRoute()).toEqual(challengeRoute);
|
||||
|
||||
// Remove challenge route
|
||||
stateManager.removeChallengeRoute('acme-challenge');
|
||||
tools.expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
tools.expect(stateManager.getActiveChallengeRoutes()).toHaveLength(0);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()).toBeNull();
|
||||
expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
expect(stateManager.getActiveChallengeRoutes()).toEqual([]);
|
||||
expect(stateManager.getPrimaryChallengeRoute()).toBeNull();
|
||||
});
|
||||
|
||||
tap.test('AcmeStateManager should track port allocations', async (tools) => {
|
||||
@ -64,27 +64,27 @@ tap.test('AcmeStateManager should track port allocations', async (tools) => {
|
||||
|
||||
// Add first route
|
||||
stateManager.addChallengeRoute(challengeRoute1);
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(8080)).toBeFalse();
|
||||
tools.expect(stateManager.getAcmePorts()).toEqual([80]);
|
||||
expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
expect(stateManager.isPortAllocatedForAcme(8080)).toBeFalse();
|
||||
expect(stateManager.getAcmePorts()).toEqual([80]);
|
||||
|
||||
// Add second route
|
||||
stateManager.addChallengeRoute(challengeRoute2);
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(8080)).toBeTrue();
|
||||
tools.expect(stateManager.getAcmePorts()).toContain(80);
|
||||
tools.expect(stateManager.getAcmePorts()).toContain(8080);
|
||||
expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
expect(stateManager.isPortAllocatedForAcme(8080)).toBeTrue();
|
||||
expect(stateManager.getAcmePorts()).toContain(80);
|
||||
expect(stateManager.getAcmePorts()).toContain(8080);
|
||||
|
||||
// Remove first route - port 80 should still be allocated
|
||||
stateManager.removeChallengeRoute('acme-challenge-1');
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(8080)).toBeTrue();
|
||||
expect(stateManager.isPortAllocatedForAcme(80)).toBeTrue();
|
||||
expect(stateManager.isPortAllocatedForAcme(8080)).toBeTrue();
|
||||
|
||||
// Remove second route - all ports should be deallocated
|
||||
stateManager.removeChallengeRoute('acme-challenge-2');
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(80)).toBeFalse();
|
||||
tools.expect(stateManager.isPortAllocatedForAcme(8080)).toBeFalse();
|
||||
tools.expect(stateManager.getAcmePorts()).toHaveLength(0);
|
||||
expect(stateManager.isPortAllocatedForAcme(80)).toBeFalse();
|
||||
expect(stateManager.isPortAllocatedForAcme(8080)).toBeFalse();
|
||||
expect(stateManager.getAcmePorts()).toEqual([]);
|
||||
});
|
||||
|
||||
tap.test('AcmeStateManager should select primary route by priority', async (tools) => {
|
||||
@ -125,19 +125,19 @@ tap.test('AcmeStateManager should select primary route by priority', async (tool
|
||||
|
||||
// Add low priority first
|
||||
stateManager.addChallengeRoute(lowPriorityRoute);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('low-priority');
|
||||
expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('low-priority');
|
||||
|
||||
// Add high priority - should become primary
|
||||
stateManager.addChallengeRoute(highPriorityRoute);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('high-priority');
|
||||
expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('high-priority');
|
||||
|
||||
// Add default priority - primary should remain high priority
|
||||
stateManager.addChallengeRoute(defaultPriorityRoute);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('high-priority');
|
||||
expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('high-priority');
|
||||
|
||||
// Remove high priority - primary should fall back to low priority
|
||||
stateManager.removeChallengeRoute('high-priority');
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('low-priority');
|
||||
expect(stateManager.getPrimaryChallengeRoute()?.name).toEqual('low-priority');
|
||||
});
|
||||
|
||||
tap.test('AcmeStateManager should handle clear operation', async (tools) => {
|
||||
@ -168,18 +168,18 @@ tap.test('AcmeStateManager should handle clear operation', async (tools) => {
|
||||
stateManager.addChallengeRoute(challengeRoute2);
|
||||
|
||||
// Verify state before clear
|
||||
tools.expect(stateManager.isChallengeRouteActive()).toBeTrue();
|
||||
tools.expect(stateManager.getActiveChallengeRoutes()).toHaveLength(2);
|
||||
tools.expect(stateManager.getAcmePorts()).toHaveLength(3);
|
||||
expect(stateManager.isChallengeRouteActive()).toBeTrue();
|
||||
expect(stateManager.getActiveChallengeRoutes()).toHaveProperty("length", 2);
|
||||
expect(stateManager.getAcmePorts()).toHaveProperty("length", 3);
|
||||
|
||||
// Clear all state
|
||||
stateManager.clear();
|
||||
|
||||
// Verify state after clear
|
||||
tools.expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
tools.expect(stateManager.getActiveChallengeRoutes()).toHaveLength(0);
|
||||
tools.expect(stateManager.getAcmePorts()).toHaveLength(0);
|
||||
tools.expect(stateManager.getPrimaryChallengeRoute()).toBeNull();
|
||||
expect(stateManager.isChallengeRouteActive()).toBeFalse();
|
||||
expect(stateManager.getActiveChallengeRoutes()).toEqual([]);
|
||||
expect(stateManager.getAcmePorts()).toEqual([]);
|
||||
expect(stateManager.getPrimaryChallengeRoute()).toBeNull();
|
||||
});
|
||||
|
||||
export default tap;
|
||||
export default tap.start();
|
@ -36,7 +36,7 @@ tap.test('should verify certificate manager callback is preserved on updateRoute
|
||||
updateCallbackSet = true;
|
||||
}
|
||||
},
|
||||
setNetworkProxy: () => {},
|
||||
setHttpProxy: () => {},
|
||||
setGlobalAcmeDefaults: () => {},
|
||||
setAcmeStateManager: () => {},
|
||||
initialize: async () => {},
|
||||
|
@ -175,7 +175,7 @@ tap.test('Route-based configuration examples', async (tools) => {
|
||||
|
||||
// Just verify that all routes are configured correctly
|
||||
console.log(`Created ${allRoutes.length} example routes`);
|
||||
expect(allRoutes.length).toEqual(8);
|
||||
expect(allRoutes.length).toEqual(10);
|
||||
});
|
||||
|
||||
export default tap.start();
|
@ -72,8 +72,8 @@ tap.test('Route Helpers - Create complete HTTPS server with redirect', async ()
|
||||
|
||||
expect(routes.length).toEqual(2);
|
||||
|
||||
// Check HTTP to HTTPS redirect
|
||||
const redirectRoute = findRouteForDomain(routes, 'full.example.com');
|
||||
// Check HTTP to HTTPS redirect - find route by action type
|
||||
const redirectRoute = routes.find(r => r.action.type === 'redirect');
|
||||
expect(redirectRoute.action.type).toEqual('redirect');
|
||||
expect(redirectRoute.match.ports).toEqual(80);
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as plugins from '../ts/plugins.js';
|
||||
import { NetworkProxy } from '../ts/proxies/network-proxy/index.js';
|
||||
import { HttpProxy } from '../ts/proxies/http-proxy/index.js';
|
||||
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
||||
import type { IRouteContext } from '../ts/core/models/route-context.js';
|
||||
|
||||
// Declare variables for tests
|
||||
let networkProxy: NetworkProxy;
|
||||
let httpProxy: HttpProxy;
|
||||
let testServer: plugins.http.Server;
|
||||
let testServerHttp2: plugins.http2.Http2Server;
|
||||
let serverPort: number;
|
||||
let serverPortHttp2: number;
|
||||
|
||||
// Setup test environment
|
||||
tap.test('setup NetworkProxy function-based targets test environment', async (tools) => {
|
||||
tap.test('setup HttpProxy function-based targets test environment', async (tools) => {
|
||||
// Set a reasonable timeout for the test
|
||||
tools.timeout = 30000; // 30 seconds
|
||||
tools.timeout(30000); // 30 seconds
|
||||
// Create simple HTTP server to respond to requests
|
||||
testServer = plugins.http.createServer((req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
@ -63,8 +63,8 @@ tap.test('setup NetworkProxy function-based targets test environment', async (to
|
||||
});
|
||||
});
|
||||
|
||||
// Create NetworkProxy instance
|
||||
networkProxy = new NetworkProxy({
|
||||
// Create HttpProxy instance
|
||||
httpProxy = new HttpProxy({
|
||||
port: 0, // Use dynamic port
|
||||
logLevel: 'info', // Use info level to see more logs
|
||||
// Disable ACME to avoid trying to bind to port 80
|
||||
@ -73,11 +73,11 @@ tap.test('setup NetworkProxy function-based targets test environment', async (to
|
||||
}
|
||||
});
|
||||
|
||||
await networkProxy.start();
|
||||
await httpProxy.start();
|
||||
|
||||
// Log the actual port being used
|
||||
const actualPort = networkProxy.getListeningPort();
|
||||
console.log(`NetworkProxy actual listening port: ${actualPort}`);
|
||||
const actualPort = httpProxy.getListeningPort();
|
||||
console.log(`HttpProxy actual listening port: ${actualPort}`);
|
||||
});
|
||||
|
||||
// Test static host/port routes
|
||||
@ -100,10 +100,10 @@ tap.test('should support static host/port routes', async () => {
|
||||
}
|
||||
];
|
||||
|
||||
await networkProxy.updateRouteConfigs(routes);
|
||||
await httpProxy.updateRouteConfigs(routes);
|
||||
|
||||
// Get proxy port using the improved getListeningPort() method
|
||||
const proxyPort = networkProxy.getListeningPort();
|
||||
const proxyPort = httpProxy.getListeningPort();
|
||||
|
||||
// Make request to proxy
|
||||
const response = await makeRequest({
|
||||
@ -145,10 +145,10 @@ tap.test('should support function-based host', async () => {
|
||||
}
|
||||
];
|
||||
|
||||
await networkProxy.updateRouteConfigs(routes);
|
||||
await httpProxy.updateRouteConfigs(routes);
|
||||
|
||||
// Get proxy port using the improved getListeningPort() method
|
||||
const proxyPort = networkProxy.getListeningPort();
|
||||
const proxyPort = httpProxy.getListeningPort();
|
||||
|
||||
// Make request to proxy
|
||||
const response = await makeRequest({
|
||||
@ -190,10 +190,10 @@ tap.test('should support function-based port', async () => {
|
||||
}
|
||||
];
|
||||
|
||||
await networkProxy.updateRouteConfigs(routes);
|
||||
await httpProxy.updateRouteConfigs(routes);
|
||||
|
||||
// Get proxy port using the improved getListeningPort() method
|
||||
const proxyPort = networkProxy.getListeningPort();
|
||||
const proxyPort = httpProxy.getListeningPort();
|
||||
|
||||
// Make request to proxy
|
||||
const response = await makeRequest({
|
||||
@ -236,10 +236,10 @@ tap.test('should support function-based host AND port', async () => {
|
||||
}
|
||||
];
|
||||
|
||||
await networkProxy.updateRouteConfigs(routes);
|
||||
await httpProxy.updateRouteConfigs(routes);
|
||||
|
||||
// Get proxy port using the improved getListeningPort() method
|
||||
const proxyPort = networkProxy.getListeningPort();
|
||||
const proxyPort = httpProxy.getListeningPort();
|
||||
|
||||
// Make request to proxy
|
||||
const response = await makeRequest({
|
||||
@ -285,10 +285,10 @@ tap.test('should support context-based routing with path', async () => {
|
||||
}
|
||||
];
|
||||
|
||||
await networkProxy.updateRouteConfigs(routes);
|
||||
await httpProxy.updateRouteConfigs(routes);
|
||||
|
||||
// Get proxy port using the improved getListeningPort() method
|
||||
const proxyPort = networkProxy.getListeningPort();
|
||||
const proxyPort = httpProxy.getListeningPort();
|
||||
|
||||
// Make request to proxy with /api path
|
||||
const apiResponse = await makeRequest({
|
||||
@ -322,9 +322,9 @@ tap.test('should support context-based routing with path', async () => {
|
||||
});
|
||||
|
||||
// Cleanup test environment
|
||||
tap.test('cleanup NetworkProxy function-based targets test environment', async () => {
|
||||
tap.test('cleanup HttpProxy function-based targets test environment', async () => {
|
||||
// Skip cleanup if setup failed
|
||||
if (!networkProxy && !testServer && !testServerHttp2) {
|
||||
if (!httpProxy && !testServer && !testServerHttp2) {
|
||||
console.log('Skipping cleanup - setup failed');
|
||||
return;
|
||||
}
|
||||
@ -358,11 +358,11 @@ tap.test('cleanup NetworkProxy function-based targets test environment', async (
|
||||
});
|
||||
}
|
||||
|
||||
// Stop NetworkProxy last
|
||||
if (networkProxy) {
|
||||
console.log('Stopping NetworkProxy...');
|
||||
await networkProxy.stop();
|
||||
console.log('NetworkProxy stopped successfully');
|
||||
// Stop HttpProxy last
|
||||
if (httpProxy) {
|
||||
console.log('Stopping HttpProxy...');
|
||||
await httpProxy.stop();
|
||||
console.log('HttpProxy stopped successfully');
|
||||
}
|
||||
|
||||
// Force exit after a short delay to ensure cleanup
|
@ -5,7 +5,7 @@ import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import { WebSocket, WebSocketServer } from 'ws';
|
||||
|
||||
let testProxy: smartproxy.NetworkProxy;
|
||||
let testProxy: smartproxy.HttpProxy;
|
||||
let testServer: http.Server;
|
||||
let wsServer: WebSocketServer;
|
||||
let testCertificates: { privateKey: string; publicKey: string };
|
||||
@ -187,7 +187,7 @@ tap.test('setup test environment', async () => {
|
||||
|
||||
tap.test('should create proxy instance', async () => {
|
||||
// Test with the original minimal options (only port)
|
||||
testProxy = new smartproxy.NetworkProxy({
|
||||
testProxy = new smartproxy.HttpProxy({
|
||||
port: 3001,
|
||||
});
|
||||
expect(testProxy).toEqual(testProxy); // Instance equality check
|
||||
@ -195,7 +195,7 @@ tap.test('should create proxy instance', async () => {
|
||||
|
||||
tap.test('should create proxy instance with extended options', async () => {
|
||||
// Test with extended options to verify backward compatibility
|
||||
testProxy = new smartproxy.NetworkProxy({
|
||||
testProxy = new smartproxy.HttpProxy({
|
||||
port: 3001,
|
||||
maxConnections: 5000,
|
||||
keepAliveTimeout: 120000,
|
||||
@ -214,7 +214,7 @@ tap.test('should create proxy instance with extended options', async () => {
|
||||
|
||||
tap.test('should start the proxy server', async () => {
|
||||
// Create a new proxy instance
|
||||
testProxy = new smartproxy.NetworkProxy({
|
||||
testProxy = new smartproxy.HttpProxy({
|
||||
port: 3001,
|
||||
maxConnections: 5000,
|
||||
backendProtocol: 'http1',
|
@ -21,7 +21,7 @@ tap.test('should not double-register port 80 when user route and ACME use same p
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'http://localhost:3000'
|
||||
target: { host: 'localhost', port: 3000 }
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -31,10 +31,10 @@ tap.test('should not double-register port 80 when user route and ACME use same p
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
target: { host: 'localhost', port: 3001 },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
certificate: 'auto' as const
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ tap.test('should not double-register port 80 when user route and ACME use same p
|
||||
(proxy as any).createCertificateManager = async function(routes: any[], certDir: string, acmeOptions: any, initialState?: any) {
|
||||
const mockCertManager = {
|
||||
setUpdateRoutesCallback: function(callback: any) { /* noop */ },
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {
|
||||
@ -122,7 +122,7 @@ tap.test('should not double-register port 80 when user route and ACME use same p
|
||||
await proxy.start();
|
||||
|
||||
// Verify that port 80 was added only once
|
||||
tools.expect(port80AddCount).toEqual(1);
|
||||
expect(port80AddCount).toEqual(1);
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
@ -146,7 +146,7 @@ tap.test('should handle ACME on different port than user routes', async (tools)
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'http://localhost:3000'
|
||||
target: { host: 'localhost', port: 3000 }
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -156,10 +156,10 @@ tap.test('should handle ACME on different port than user routes', async (tools)
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
target: { host: 'localhost', port: 3001 },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
certificate: 'auto' as const
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -202,7 +202,7 @@ tap.test('should handle ACME on different port than user routes', async (tools)
|
||||
(proxy as any).createCertificateManager = async function(routes: any[], certDir: string, acmeOptions: any, initialState?: any) {
|
||||
const mockCertManager = {
|
||||
setUpdateRoutesCallback: function(callback: any) { /* noop */ },
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {
|
||||
@ -243,11 +243,11 @@ tap.test('should handle ACME on different port than user routes', async (tools)
|
||||
await proxy.start();
|
||||
|
||||
// Verify that all expected ports were added
|
||||
tools.expect(portAddHistory).toContain(80); // User route
|
||||
tools.expect(portAddHistory).toContain(443); // TLS route
|
||||
tools.expect(portAddHistory).toContain(8080); // ACME challenge on different port
|
||||
expect(portAddHistory.includes(80)).toBeTrue(); // User route
|
||||
expect(portAddHistory.includes(443)).toBeTrue(); // TLS route
|
||||
expect(portAddHistory.includes(8080)).toBeTrue(); // ACME challenge on different port
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
export default tap;
|
||||
export default tap.start();
|
@ -1,5 +1,5 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartProxy } from '../ts/index.js';
|
||||
import { SmartProxy, type IRouteConfig } from '../ts/index.js';
|
||||
|
||||
/**
|
||||
* Test that verifies mutex prevents race conditions during concurrent route updates
|
||||
@ -42,10 +42,10 @@ tap.test('should handle concurrent route updates without race conditions', async
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `https://localhost:${3001 + i}`,
|
||||
target: { host: 'localhost', port: 3001 + i },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
certificate: 'auto' as const
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ tap.test('should handle concurrent route updates without race conditions', async
|
||||
|
||||
// Verify final state
|
||||
const currentRoutes = proxy['settings'].routes;
|
||||
tools.expect(currentRoutes.length).toEqual(2); // Initial route + last update
|
||||
expect(currentRoutes.length).toEqual(2); // Initial route + last update
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
@ -95,7 +95,7 @@ tap.test('should serialize route updates with mutex', async (tools) => {
|
||||
maxConcurrent = Math.max(maxConcurrent, concurrent);
|
||||
|
||||
// If mutex is working, only one update should run at a time
|
||||
tools.expect(concurrent).toEqual(1);
|
||||
expect(concurrent).toEqual(1);
|
||||
|
||||
const result = await originalUpdateRoutes(routes);
|
||||
updateEndCount++;
|
||||
@ -121,9 +121,9 @@ tap.test('should serialize route updates with mutex', async (tools) => {
|
||||
await Promise.all(updates);
|
||||
|
||||
// All updates should have completed
|
||||
tools.expect(updateStartCount).toEqual(5);
|
||||
tools.expect(updateEndCount).toEqual(5);
|
||||
tools.expect(maxConcurrent).toEqual(1); // Mutex ensures only one at a time
|
||||
expect(updateStartCount).toEqual(5);
|
||||
expect(updateEndCount).toEqual(5);
|
||||
expect(maxConcurrent).toEqual(1); // Mutex ensures only one at a time
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
@ -141,10 +141,10 @@ tap.test('should preserve challenge route state during cert manager recreation',
|
||||
match: { ports: [443] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
target: { host: 'localhost', port: 3001 },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
certificate: 'auto' as const
|
||||
}
|
||||
}
|
||||
}],
|
||||
@ -167,31 +167,31 @@ tap.test('should preserve challenge route state during cert manager recreation',
|
||||
await proxy.start();
|
||||
|
||||
// Initial creation
|
||||
tools.expect(certManagerCreationCount).toEqual(1);
|
||||
expect(certManagerCreationCount).toEqual(1);
|
||||
|
||||
// Multiple route updates
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await proxy.updateRoutes([
|
||||
...settings.routes,
|
||||
...settings.routes as IRouteConfig[],
|
||||
{
|
||||
name: `dynamic-route-${i}`,
|
||||
match: { ports: [9000 + i] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${5000 + i}`
|
||||
target: { host: 'localhost', port: 5000 + i }
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Certificate manager should be recreated for each update
|
||||
tools.expect(certManagerCreationCount).toEqual(4); // 1 initial + 3 updates
|
||||
expect(certManagerCreationCount).toEqual(4); // 1 initial + 3 updates
|
||||
|
||||
// State should be preserved (challenge route active)
|
||||
const globalState = proxy['globalChallengeRouteActive'];
|
||||
tools.expect(globalState).toBeDefined();
|
||||
expect(globalState).toBeDefined();
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
export default tap;
|
||||
export default tap.start();
|
@ -4,6 +4,11 @@ import { SmartProxy } from '../ts/index.js';
|
||||
tap.test('should set update routes callback on certificate manager', async () => {
|
||||
// Create a simple proxy with a route requiring certificates
|
||||
const proxy = new SmartProxy({
|
||||
acme: {
|
||||
email: 'test@local.dev',
|
||||
useProduction: false,
|
||||
port: 8080 // Use non-privileged port for ACME challenges globally
|
||||
},
|
||||
routes: [{
|
||||
name: 'test-route',
|
||||
match: {
|
||||
|
98
test/test.route-redirects.ts
Normal file
98
test/test.route-redirects.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { SmartProxy } from '../ts/proxies/smart-proxy/index.js';
|
||||
import { createHttpToHttpsRedirect } from '../ts/proxies/smart-proxy/utils/route-helpers.js';
|
||||
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
||||
|
||||
// Test that HTTP to HTTPS redirects work correctly
|
||||
tap.test('should handle HTTP to HTTPS redirects', async (tools) => {
|
||||
// Create a simple HTTP to HTTPS redirect route
|
||||
const redirectRoute = createHttpToHttpsRedirect(
|
||||
'example.com',
|
||||
443,
|
||||
{
|
||||
name: 'HTTP to HTTPS Redirect Test'
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the route is configured correctly
|
||||
expect(redirectRoute.action.type).toEqual('redirect');
|
||||
expect(redirectRoute.action.redirect).toBeTruthy();
|
||||
expect(redirectRoute.action.redirect?.to).toEqual('https://{domain}:443{path}');
|
||||
expect(redirectRoute.action.redirect?.status).toEqual(301);
|
||||
expect(redirectRoute.match.ports).toEqual(80);
|
||||
expect(redirectRoute.match.domains).toEqual('example.com');
|
||||
});
|
||||
|
||||
tap.test('should handle custom redirect configurations', async (tools) => {
|
||||
// Create a custom redirect route
|
||||
const customRedirect: IRouteConfig = {
|
||||
name: 'custom-redirect',
|
||||
match: {
|
||||
ports: [8080],
|
||||
domains: ['old.example.com']
|
||||
},
|
||||
action: {
|
||||
type: 'redirect',
|
||||
redirect: {
|
||||
to: 'https://new.example.com{path}',
|
||||
status: 302
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Verify the route structure
|
||||
expect(customRedirect.action.redirect?.to).toEqual('https://new.example.com{path}');
|
||||
expect(customRedirect.action.redirect?.status).toEqual(302);
|
||||
});
|
||||
|
||||
tap.test('should support multiple redirect scenarios', async (tools) => {
|
||||
const routes: IRouteConfig[] = [
|
||||
// HTTP to HTTPS redirect
|
||||
createHttpToHttpsRedirect(['example.com', 'www.example.com']),
|
||||
|
||||
// Custom redirect with different port
|
||||
{
|
||||
name: 'custom-port-redirect',
|
||||
match: {
|
||||
ports: 8080,
|
||||
domains: 'api.example.com'
|
||||
},
|
||||
action: {
|
||||
type: 'redirect',
|
||||
redirect: {
|
||||
to: 'https://{domain}:8443{path}',
|
||||
status: 308
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Redirect to different domain entirely
|
||||
{
|
||||
name: 'domain-redirect',
|
||||
match: {
|
||||
ports: 80,
|
||||
domains: 'old-domain.com'
|
||||
},
|
||||
action: {
|
||||
type: 'redirect',
|
||||
redirect: {
|
||||
to: 'https://new-domain.com{path}',
|
||||
status: 301
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Create SmartProxy with redirect routes
|
||||
const proxy = new SmartProxy({
|
||||
routes
|
||||
});
|
||||
|
||||
// Verify all routes are redirect type
|
||||
routes.forEach(route => {
|
||||
expect(route.action.type).toEqual('redirect');
|
||||
expect(route.action.redirect).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
export default tap.start();
|
@ -53,7 +53,7 @@ tap.test('should preserve route update callback after updateRoutes', async () =>
|
||||
this.updateRoutesCallback = callback;
|
||||
},
|
||||
updateRoutesCallback: null,
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {
|
||||
@ -110,7 +110,7 @@ tap.test('should preserve route update callback after updateRoutes', async () =>
|
||||
this.updateRoutesCallback = callback;
|
||||
},
|
||||
updateRoutesCallback: null,
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {},
|
||||
@ -231,7 +231,7 @@ tap.test('should handle route updates when cert manager is not initialized', asy
|
||||
this.updateRoutesCallback = callback;
|
||||
},
|
||||
updateRoutesCallback: null,
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
initialize: async function() {},
|
||||
stop: async function() {},
|
||||
getAcmeOptions: function() {
|
||||
@ -291,7 +291,7 @@ tap.test('real code integration test - verify fix is applied', async () => {
|
||||
this.updateRoutesCallback = callback;
|
||||
},
|
||||
updateRoutesCallback: null as any,
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {},
|
||||
|
@ -1,7 +1,7 @@
|
||||
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/http/router/proxy-router.js';
|
||||
import { ProxyRouter, type RouterResult } from '../ts/routing/router/proxy-router.js';
|
||||
|
||||
// Test proxies and configurations
|
||||
let router: ProxyRouter;
|
||||
|
@ -14,7 +14,7 @@ tap.test('should create ACME challenge route', async (tools) => {
|
||||
{
|
||||
name: 'secure-route',
|
||||
match: {
|
||||
ports: [443],
|
||||
ports: [8443],
|
||||
domains: 'test.example.com'
|
||||
},
|
||||
action: {
|
||||
@ -22,14 +22,20 @@ tap.test('should create ACME challenge route', async (tools) => {
|
||||
target: { host: 'localhost', port: 8080 },
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto',
|
||||
certificate: 'auto' as const,
|
||||
acme: {
|
||||
email: 'test@test.local' // Use non-example.com domain
|
||||
email: 'ssl@bleu.de',
|
||||
challengePort: 8080 // Use non-privileged port for challenges
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
acme: {
|
||||
email: 'ssl@bleu.de',
|
||||
port: 8080, // Use non-privileged port globally
|
||||
useProduction: false
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
@ -42,7 +48,7 @@ tap.test('should create ACME challenge route', async (tools) => {
|
||||
setUpdateRoutesCallback: function(callback: any) {
|
||||
updateRoutesCallback = callback;
|
||||
},
|
||||
setNetworkProxy: function() {},
|
||||
setHttpProxy: function() {},
|
||||
setGlobalAcmeDefaults: function() {},
|
||||
setAcmeStateManager: function() {},
|
||||
initialize: async function() {
|
||||
@ -52,7 +58,7 @@ tap.test('should create ACME challenge route', async (tools) => {
|
||||
name: 'acme-challenge',
|
||||
priority: 1000,
|
||||
match: {
|
||||
ports: 80,
|
||||
ports: 8080,
|
||||
path: '/.well-known/acme-challenge/*'
|
||||
},
|
||||
action: {
|
||||
@ -96,7 +102,7 @@ tap.test('should create ACME challenge route', async (tools) => {
|
||||
|
||||
expect(challengeRoute).toBeDefined();
|
||||
expect(challengeRoute.match.path).toEqual('/.well-known/acme-challenge/*');
|
||||
expect(challengeRoute.match.ports).toEqual(80);
|
||||
expect(challengeRoute.match.ports).toEqual(8080);
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
Reference in New Issue
Block a user