smartproxy/test/test.logger-error-handling.ts
2025-05-20 15:44:48 +00:00

197 lines
6.0 KiB
TypeScript

import * as plugins from '../ts/plugins.js';
import { SmartProxy } from '../ts/index.js';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { logger } from '../ts/core/utils/logger.js';
// Store the original logger reference
let originalLogger: any = logger;
let mockLogger: any;
// Create test routes using high ports to avoid permission issues
const createRoute = (id: number, domain: string, port: number = 8443) => ({
name: `test-route-${id}`,
match: {
ports: [port],
domains: [domain]
},
action: {
type: 'forward' as const,
target: {
host: 'localhost',
port: 3000 + id
},
tls: {
mode: 'terminate' as const,
certificate: 'auto' as const,
acme: {
email: 'test@testdomain.test',
useProduction: false
}
}
}
});
let testProxy: SmartProxy;
tap.test('should setup test proxy for logger error handling tests', async () => {
// Create a proxy for testing
testProxy = new SmartProxy({
routes: [createRoute(1, 'test1.error-handling.test', 8443)],
acme: {
email: 'test@testdomain.test',
useProduction: false,
port: 8080
}
});
// Mock the certificate manager to avoid actual ACME initialization
const originalCreateCertManager = (testProxy as any).createCertificateManager;
(testProxy as any).createCertificateManager = async function(routes: any[], certDir: string, acmeOptions: any, initialState?: any) {
const mockCertManager = {
setUpdateRoutesCallback: function(callback: any) {
this.updateRoutesCallback = callback;
},
updateRoutesCallback: null as any,
setHttpProxy: function() {},
setGlobalAcmeDefaults: function() {},
setAcmeStateManager: function() {},
initialize: async function() {},
provisionAllCertificates: async function() {},
stop: async function() {},
getAcmeOptions: function() {
return acmeOptions || { email: 'test@testdomain.test', useProduction: false };
},
getState: function() {
return initialState || { challengeRouteActive: false };
}
};
// Always set up the route update callback for ACME challenges
mockCertManager.setUpdateRoutesCallback(async (routes) => {
await this.updateRoutes(routes);
});
return mockCertManager;
};
// Mock initializeCertificateManager as well
(testProxy as any).initializeCertificateManager = async function() {
// Create mock cert manager using the method above
this.certManager = await this.createCertificateManager(
this.settings.routes,
'./certs',
{ email: 'test@testdomain.test', useProduction: false }
);
};
// Start the proxy with mocked components
await testProxy.start();
expect(testProxy).toBeTruthy();
});
tap.test('should handle logger errors in updateRoutes without failing', async () => {
// Temporarily inject the mock logger that throws errors
const origConsoleLog = console.log;
let consoleLogCalled = false;
// Spy on console.log to verify it's used as fallback
console.log = (...args: any[]) => {
consoleLogCalled = true;
// Call original implementation but mute the output for tests
// origConsoleLog(...args);
};
try {
// Create mock logger that throws
mockLogger = {
log: () => {
throw new Error('Simulated logger error');
}
};
// Override the logger in the imported module
// This is a hack but necessary for testing
(global as any).logger = mockLogger;
// Access the internal logger used by SmartProxy
const smartProxyImport = await import('../ts/proxies/smart-proxy/smart-proxy.js');
// @ts-ignore
smartProxyImport.logger = mockLogger;
// Update routes - this should not fail even with logger errors
const newRoutes = [
createRoute(1, 'test1.error-handling.test', 8443),
createRoute(2, 'test2.error-handling.test', 8444)
];
await testProxy.updateRoutes(newRoutes);
// Verify that the update was successful
expect((testProxy as any).settings.routes.length).toEqual(2);
expect(consoleLogCalled).toEqual(true);
} finally {
// Always restore console.log and logger
console.log = origConsoleLog;
(global as any).logger = originalLogger;
}
});
tap.test('should handle logger errors in certificate manager callbacks', async () => {
// Temporarily inject the mock logger that throws errors
const origConsoleLog = console.log;
let consoleLogCalled = false;
// Spy on console.log to verify it's used as fallback
console.log = (...args: any[]) => {
consoleLogCalled = true;
// Call original implementation but mute the output for tests
// origConsoleLog(...args);
};
try {
// Create mock logger that throws
mockLogger = {
log: () => {
throw new Error('Simulated logger error');
}
};
// Override the logger in the imported module
// This is a hack but necessary for testing
(global as any).logger = mockLogger;
// Access the cert manager and trigger the updateRoutesCallback
const certManager = (testProxy as any).certManager;
expect(certManager).toBeTruthy();
expect(certManager.updateRoutesCallback).toBeTruthy();
// Call the certificate manager's updateRoutesCallback directly
const challengeRoute = {
name: 'acme-challenge',
match: {
ports: [8080],
path: '/.well-known/acme-challenge/*'
},
action: {
type: 'static' as const,
content: 'mock-challenge-content'
}
};
// This should not throw, despite logger errors
await certManager.updateRoutesCallback([...testProxy.settings.routes, challengeRoute]);
// Verify console.log was used as fallback
expect(consoleLogCalled).toEqual(true);
} finally {
// Always restore console.log and logger
console.log = origConsoleLog;
(global as any).logger = originalLogger;
}
});
tap.test('should clean up properly', async () => {
await testProxy.stop();
});
tap.start();