import { describe, it, expect } from 'vitest'; import { composeInboundTag, isAutoInboundTag, type InboundTagInput } from '@/lib/xray/inbound-tag'; // Parity with web/service/port_conflict.go TestInboundTransports: the L4 suffix // the tag encodes must match the Go service so the form preview agrees with the // tag the backend re-derives on save. describe('composeInboundTag transport suffix parity', () => { const base = (over: Partial): InboundTagInput => ({ port: 443, nodeId: null, protocol: 'vless', ...over, }); const cases: Array<[string, InboundTagInput, string]> = [ ['vless tcp', base({ streamSettings: { network: 'tcp' } }), 'in-443-tcp'], ['vless ws (still tcp)', base({ streamSettings: { network: 'ws' } }), 'in-443-tcp'], ['vless kcp is udp', base({ streamSettings: { network: 'kcp' } }), 'in-443-udp'], ['vless quic is udp', base({ streamSettings: { network: 'quic' } }), 'in-443-udp'], ['vless empty stream defaults tcp', base({}), 'in-443-tcp'], ['vmess tcp', base({ protocol: 'vmess', streamSettings: { network: 'tcp' } }), 'in-443-tcp'], ['trojan grpc is tcp', base({ protocol: 'trojan', streamSettings: { network: 'grpc' } }), 'in-443-tcp'], ['hysteria forced udp', base({ protocol: 'hysteria', streamSettings: { network: 'tcp' } }), 'in-443-udp'], ['wireguard forced udp', base({ protocol: 'wireguard' }), 'in-443-udp'], ['shadowsocks tcp,udp', base({ protocol: 'shadowsocks', settings: { network: 'tcp,udp' } }), 'in-443-tcpudp'], ['shadowsocks udp only', base({ protocol: 'shadowsocks', settings: { network: 'udp' } }), 'in-443-udp'], ['shadowsocks tcp only', base({ protocol: 'shadowsocks', settings: { network: 'tcp' } }), 'in-443-tcp'], ['mixed udp on', base({ protocol: 'mixed', streamSettings: { network: 'tcp' }, settings: { udp: true } }), 'in-443-tcpudp'], ['mixed udp off', base({ protocol: 'mixed', streamSettings: { network: 'tcp' }, settings: { udp: false } }), 'in-443-tcp'], ['tunnel allowedNetwork udp', base({ protocol: 'tunnel', settings: { allowedNetwork: 'udp' } }), 'in-443-udp'], ]; it.each(cases)('%s', (_name, input, want) => { expect(composeInboundTag(input)).toBe(want); }); it('ignores the listen address and adds the node prefix', () => { expect(composeInboundTag(base({ port: 8443, streamSettings: { network: 'tcp' } }))) .toBe('in-8443-tcp'); expect(composeInboundTag(base({ nodeId: 1, port: 443, streamSettings: { network: 'tcp' } }))) .toBe('n1-in-443-tcp'); }); }); // Parity with TestIsAutoGeneratedTag. describe('isAutoInboundTag', () => { const input: InboundTagInput = { port: 443, nodeId: null, protocol: 'vless', streamSettings: { network: 'tcp' }, }; it('recognises canonical, dedup-suffixed and empty as auto', () => { expect(isAutoInboundTag('in-443-tcp', input)).toBe(true); expect(isAutoInboundTag('in-443-tcp-2', input)).toBe(true); expect(isAutoInboundTag('', input)).toBe(true); }); it('treats custom / stale / malformed-suffix tags as not auto', () => { expect(isAutoInboundTag('my-custom', input)).toBe(false); expect(isAutoInboundTag('in-8443-tcp', input)).toBe(false); expect(isAutoInboundTag('in-443-tcp-x', input)).toBe(false); expect(isAutoInboundTag('in-443-tcp-', input)).toBe(false); }); });