|
|
@@ -0,0 +1,65 @@
|
|
|
+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>): InboundTagInput => ({
|
|
|
+ listen: '0.0.0.0',
|
|
|
+ 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('scopes a non-any listen and node prefix', () => {
|
|
|
+ expect(composeInboundTag(base({ listen: '127.0.0.1', port: 8443, streamSettings: { network: 'tcp' } })))
|
|
|
+ .toBe('in-127.0.0.1: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 = {
|
|
|
+ listen: '0.0.0.0', 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);
|
|
|
+ });
|
|
|
+});
|