import { describe, expect, it } from 'vitest'; import { genWireguardConfigs, genWireguardLinks } from '@/lib/xray/inbound-link'; import { InboundSchema } from '@/schemas/api/inbound'; // Multi-client WireGuard renders one link/config per entry in settings.clients // (the canonical store), not settings.peers. Each client carries its own // privateKey + allowedIPs; the server public key is derived from secretKey. function wgInbound() { return InboundSchema.parse({ id: 90, remark: 'wg-mc', port: 51820, protocol: 'wireguard', settings: { mtu: 1420, secretKey: 'iJ2cBkrSGqRwIfYIDIxk7hr5RXfdR93MfJUL7yqkkH8=', peers: [], clients: [ { email: 'alice', privateKey: 'QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=', publicKey: 'DGSYIcEKAUkA7HhzGSjxLZuV67BR3LeyU0BMLJzNVHQ=', allowedIPs: ['10.0.0.2/32'], keepAlive: 25, }, { email: 'bob', privateKey: 'aGVsbG8td29ybGQtdGVzdC1wcml2YXRlLWtleS1ub3chIQ==', publicKey: 'b3RoZXItcHVibGljLWtleS1mb3ItYm9iLXRlc3QtdmFsISE=', allowedIPs: ['10.0.0.3/32'], }, ], }, }); } describe('wireguard multi-client link/config fan-out', () => { it('emits one link per client from settings.clients', () => { const out = genWireguardLinks({ inbound: wgInbound(), remark: 'wg-mc', fallbackHostname: 'wg.example.test', }); const links = out.split('\r\n').filter(Boolean); expect(links).toHaveLength(2); expect(links[0]).toContain('wireguard://'); expect(links[0]).toContain('address=10.0.0.2%2F32'); expect(links[1]).toContain('address=10.0.0.3%2F32'); }); it('emits one .conf per client with its own address', () => { const out = genWireguardConfigs({ inbound: wgInbound(), remark: 'wg-mc', fallbackHostname: 'wg.example.test', }); const configs = out.split('\r\n[Interface]').length; expect(out).toContain('Address = 10.0.0.2/32'); expect(out).toContain('Address = 10.0.0.3/32'); expect(configs).toBe(2); }); it('falls back to settings.peers for legacy single-config inbounds', () => { const legacy = InboundSchema.parse({ id: 91, remark: 'wg-legacy', port: 51820, protocol: 'wireguard', settings: { secretKey: 'iJ2cBkrSGqRwIfYIDIxk7hr5RXfdR93MfJUL7yqkkH8=', peers: [ { privateKey: 'QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=', publicKey: 'DGSYIcEKAUkA7HhzGSjxLZuV67BR3LeyU0BMLJzNVHQ=', allowedIPs: ['10.0.0.9/32'], }, ], }, }); const out = genWireguardLinks({ inbound: legacy, remark: 'wg-legacy', fallbackHostname: 'wg.example.test' }); const links = out.split('\r\n').filter(Boolean); expect(links).toHaveLength(1); expect(links[0]).toContain('address=10.0.0.9%2F32'); }); });