import { tap, expect } from '@push.rocks/tapbundle'; import * as plugins from '../ts/plugins.js'; import { CertProvisioner } from '../ts/certificate/providers/cert-provisioner.js'; import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js'; import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js'; import type { ICertificateData } from '../ts/certificate/models/certificate-types.js'; // Import SmartProxyCertProvisionObject type alias import type { TSmartProxyCertProvisionObject } from '../ts/certificate/providers/cert-provisioner.js'; // Fake Port80Handler stub class FakePort80Handler extends plugins.EventEmitter { public domainsAdded: string[] = []; public renewCalled: string[] = []; addDomain(opts: { domainName: string; sslRedirect: boolean; acmeMaintenance: boolean }) { this.domainsAdded.push(opts.domainName); } async renewCertificate(domain: string): Promise { this.renewCalled.push(domain); } } // Fake NetworkProxyBridge stub class FakeNetworkProxyBridge { public appliedCerts: ICertificateData[] = []; applyExternalCertificate(cert: ICertificateData) { this.appliedCerts.push(cert); } } tap.test('CertProvisioner handles static provisioning', async () => { const domain = 'static.com'; // Create route-based configuration for testing const routeConfigs: IRouteConfig[] = [{ match: { ports: 443, domains: [domain] }, action: { type: 'forward', target: { host: 'localhost', port: 443 }, tls: { mode: 'terminate-and-reencrypt', certificate: 'auto' } } }]; const fakePort80 = new FakePort80Handler(); const fakeBridge = new FakeNetworkProxyBridge(); // certProvider returns static certificate const certProvider = async (d: string): Promise => { expect(d).toEqual(domain); return { domainName: domain, publicKey: 'CERT', privateKey: 'KEY', validUntil: Date.now() + 3600 * 1000, created: Date.now(), csr: 'CSR', id: 'ID', }; }; const prov = new CertProvisioner( routeConfigs, fakePort80 as any, fakeBridge as any, certProvider, 1, // low renew threshold 1, // short interval false // disable auto renew for unit test ); const events: any[] = []; prov.on('certificate', (data) => events.push(data)); await prov.start(); // Static flow: no addDomain, certificate applied via bridge expect(fakePort80.domainsAdded.length).toEqual(0); expect(fakeBridge.appliedCerts.length).toEqual(1); expect(events.length).toEqual(1); const evt = events[0]; expect(evt.domain).toEqual(domain); expect(evt.certificate).toEqual('CERT'); expect(evt.privateKey).toEqual('KEY'); expect(evt.isRenewal).toEqual(false); expect(evt.source).toEqual('static'); }); tap.test('CertProvisioner handles http01 provisioning', async () => { const domain = 'http01.com'; // Create route-based configuration for testing const routeConfigs: IRouteConfig[] = [{ match: { ports: 443, domains: [domain] }, action: { type: 'forward', target: { host: 'localhost', port: 80 }, tls: { mode: 'terminate', certificate: 'auto' } } }]; const fakePort80 = new FakePort80Handler(); const fakeBridge = new FakeNetworkProxyBridge(); // certProvider returns http01 directive const certProvider = async (): Promise => 'http01'; const prov = new CertProvisioner( routeConfigs, fakePort80 as any, fakeBridge as any, certProvider, 1, 1, false ); const events: any[] = []; prov.on('certificate', (data) => events.push(data)); await prov.start(); // HTTP-01 flow: addDomain called, no static cert applied expect(fakePort80.domainsAdded).toEqual([domain]); expect(fakeBridge.appliedCerts.length).toEqual(0); expect(events.length).toEqual(0); }); tap.test('CertProvisioner on-demand http01 renewal', async () => { const domain = 'renew.com'; // Create route-based configuration for testing const routeConfigs: IRouteConfig[] = [{ match: { ports: 443, domains: [domain] }, action: { type: 'forward', target: { host: 'localhost', port: 80 }, tls: { mode: 'terminate', certificate: 'auto' } } }]; const fakePort80 = new FakePort80Handler(); const fakeBridge = new FakeNetworkProxyBridge(); const certProvider = async (): Promise => 'http01'; const prov = new CertProvisioner( routeConfigs, fakePort80 as any, fakeBridge as any, certProvider, 1, 1, false ); // requestCertificate should call renewCertificate await prov.requestCertificate(domain); expect(fakePort80.renewCalled).toEqual([domain]); }); tap.test('CertProvisioner on-demand static provisioning', async () => { const domain = 'ondemand.com'; // Create route-based configuration for testing const routeConfigs: IRouteConfig[] = [{ match: { ports: 443, domains: [domain] }, action: { type: 'forward', target: { host: 'localhost', port: 443 }, tls: { mode: 'terminate-and-reencrypt', certificate: 'auto' } } }]; const fakePort80 = new FakePort80Handler(); const fakeBridge = new FakeNetworkProxyBridge(); const certProvider = async (): Promise => ({ domainName: domain, publicKey: 'PKEY', privateKey: 'PRIV', validUntil: Date.now() + 1000, created: Date.now(), csr: 'CSR', id: 'ID', }); const prov = new CertProvisioner( routeConfigs, fakePort80 as any, fakeBridge as any, certProvider, 1, 1, false ); const events: any[] = []; prov.on('certificate', (data) => events.push(data)); await prov.requestCertificate(domain); expect(fakeBridge.appliedCerts.length).toEqual(1); expect(events.length).toEqual(1); expect(events[0].domain).toEqual(domain); expect(events[0].source).toEqual('static'); }); export default tap.start();