import { tap, expect } from '@git.zone/tstest/tapbundle'; import { SmartProxy } from '../ts/index.js'; // Test that certificate provisioning is deferred until after ports are listening tap.test('should defer certificate provisioning until ports are ready', async (tapTest) => { // Track when operations happen let portsListening = false; let certProvisioningStarted = false; let operationOrder: string[] = []; // Create proxy with certificate route but without real ACME const proxy = new SmartProxy({ routes: [{ name: 'test-route', match: { ports: 8443, domains: ['test.local'] }, action: { type: 'forward', target: { host: 'localhost', port: 8181 }, tls: { mode: 'terminate', certificate: 'auto', acme: { email: 'test@local.dev', useProduction: false } } } }] }); // Override the certificate manager creation to avoid real ACME const originalCreateCertManager = proxy['createCertificateManager']; proxy['createCertificateManager'] = async function(...args: any[]) { console.log('Creating mock cert manager'); operationOrder.push('create-cert-manager'); const mockCertManager = { initialize: async () => { operationOrder.push('cert-manager-init'); console.log('Mock cert manager initialized'); }, provisionAllCertificates: async () => { operationOrder.push('cert-provisioning'); certProvisioningStarted = true; // Check that ports are listening when provisioning starts if (!portsListening) { throw new Error('Certificate provisioning started before ports ready!'); } console.log('Mock certificate provisioning (ports are ready)'); }, stop: async () => {}, setHttpProxy: () => {}, setGlobalAcmeDefaults: () => {}, setAcmeStateManager: () => {}, setUpdateRoutesCallback: () => {}, getAcmeOptions: () => ({}), getState: () => ({ challengeRouteActive: false }) }; // Call initialize immediately as the real createCertificateManager does await mockCertManager.initialize(); return mockCertManager; }; // Track port manager operations const originalAddPorts = proxy['portManager'].addPorts; proxy['portManager'].addPorts = async function(ports: number[]) { operationOrder.push('ports-starting'); const result = await originalAddPorts.call(this, ports); operationOrder.push('ports-ready'); portsListening = true; console.log('Ports are now listening'); return result; }; // Start the proxy await proxy.start(); // Log the operation order for debugging console.log('Operation order:', operationOrder); // Verify operations happened in the correct order expect(operationOrder).toContain('create-cert-manager'); expect(operationOrder).toContain('cert-manager-init'); expect(operationOrder).toContain('ports-starting'); expect(operationOrder).toContain('ports-ready'); expect(operationOrder).toContain('cert-provisioning'); // Verify ports were ready before certificate provisioning const portsReadyIndex = operationOrder.indexOf('ports-ready'); const certProvisioningIndex = operationOrder.indexOf('cert-provisioning'); expect(portsReadyIndex).toBeLessThan(certProvisioningIndex); expect(certProvisioningStarted).toEqual(true); expect(portsListening).toEqual(true); await proxy.stop(); }); tap.start();