140 lines
4.7 KiB
TypeScript
140 lines
4.7 KiB
TypeScript
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||
|
import * as plugins from '../ts/plugins.js';
|
||
|
import { CertProvisioner } from '../ts/smartproxy/classes.pp.certprovisioner.js';
|
||
|
import type { IDomainConfig, ISmartProxyCertProvisionObject } from '../ts/smartproxy/classes.pp.interfaces.js';
|
||
|
import type { ICertificateData } from '../ts/port80handler/classes.port80handler.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<void> {
|
||
|
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';
|
||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||
|
const fakePort80 = new FakePort80Handler();
|
||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||
|
// certProvider returns static certificate
|
||
|
const certProvider = async (d: string): Promise<ISmartProxyCertProvisionObject> => {
|
||
|
expect(d).toEqual(domain);
|
||
|
return {
|
||
|
domainName: domain,
|
||
|
publicKey: 'CERT',
|
||
|
privateKey: 'KEY',
|
||
|
validUntil: Date.now() + 3600 * 1000
|
||
|
};
|
||
|
};
|
||
|
const prov = new CertProvisioner(
|
||
|
domainConfigs,
|
||
|
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';
|
||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||
|
const fakePort80 = new FakePort80Handler();
|
||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||
|
// certProvider returns http01 directive
|
||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => 'http01';
|
||
|
const prov = new CertProvisioner(
|
||
|
domainConfigs,
|
||
|
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';
|
||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||
|
const fakePort80 = new FakePort80Handler();
|
||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => 'http01';
|
||
|
const prov = new CertProvisioner(
|
||
|
domainConfigs,
|
||
|
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';
|
||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||
|
const fakePort80 = new FakePort80Handler();
|
||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => ({
|
||
|
domainName: domain,
|
||
|
publicKey: 'PKEY',
|
||
|
privateKey: 'PRIV',
|
||
|
validUntil: Date.now() + 1000
|
||
|
});
|
||
|
const prov = new CertProvisioner(
|
||
|
domainConfigs,
|
||
|
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();
|