inbound-defaults.test.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import { describe, expect, it } from 'vitest';
  2. import {
  3. createDefaultHttpInboundSettings,
  4. createDefaultHysteriaClient,
  5. createDefaultHysteriaInboundSettings,
  6. createDefaultMixedInboundSettings,
  7. createDefaultShadowsocksClient,
  8. createDefaultShadowsocksInboundSettings,
  9. createDefaultTrojanClient,
  10. createDefaultTrojanInboundSettings,
  11. createDefaultTuicClient,
  12. createDefaultTuicInboundSettings,
  13. createDefaultTunnelInboundSettings,
  14. createDefaultVlessClient,
  15. createDefaultVlessInboundSettings,
  16. createDefaultVmessClient,
  17. createDefaultVmessInboundSettings,
  18. createDefaultWireguardInboundSettings,
  19. } from '@/lib/xray/inbound-defaults';
  20. import { createHysteriaTlsSettingsWithDefaultCert } from '@/lib/xray/inbound-tls-defaults';
  21. import { HttpInboundSettingsSchema } from '@/schemas/protocols/inbound/http';
  22. import {
  23. HysteriaClientSchema,
  24. HysteriaInboundSettingsSchema,
  25. } from '@/schemas/protocols/inbound/hysteria';
  26. import { MixedInboundSettingsSchema } from '@/schemas/protocols/inbound/mixed';
  27. import {
  28. ShadowsocksClientSchema,
  29. ShadowsocksInboundSettingsSchema,
  30. } from '@/schemas/protocols/inbound/shadowsocks';
  31. import {
  32. TrojanClientSchema,
  33. TrojanInboundSettingsSchema,
  34. } from '@/schemas/protocols/inbound/trojan';
  35. import { TuicClientSchema, TuicInboundSettingsSchema } from '@/schemas/protocols/inbound/tuic';
  36. import { TunnelInboundSettingsSchema } from '@/schemas/protocols/inbound/tunnel';
  37. import { VlessClientSchema, VlessInboundSettingsSchema } from '@/schemas/protocols/inbound/vless';
  38. import { VmessClientSchema, VmessInboundSettingsSchema } from '@/schemas/protocols/inbound/vmess';
  39. import { WireguardInboundSettingsSchema } from '@/schemas/protocols/inbound/wireguard';
  40. // Tests pass explicit seeds for every random field so the assertions don't
  41. // depend on window.crypto (the node test env has no crypto.randomUUID).
  42. // Each factory is verified two ways:
  43. // 1. snapshot — locks the exact shape
  44. // 2. Zod parse round-trip — confirms the factory output is a valid
  45. // member of the protocol's client schema (no missing defaults, no
  46. // stray fields)
  47. const seed = {
  48. email: '[email protected]',
  49. subId: 'fixed-sub-id-1234',
  50. };
  51. describe('createDefaultVlessClient', () => {
  52. it('produces a Zod-valid client', () => {
  53. const c = createDefaultVlessClient({ ...seed, id: '11111111-2222-4333-8444-555555555555' });
  54. expect(c).toMatchSnapshot();
  55. expect(VlessClientSchema.parse(c)).toEqual(c);
  56. });
  57. });
  58. describe('createDefaultVmessClient', () => {
  59. it('produces a Zod-valid client', () => {
  60. const c = createDefaultVmessClient({ ...seed, id: 'aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee' });
  61. expect(c).toMatchSnapshot();
  62. expect(VmessClientSchema.parse(c)).toEqual(c);
  63. });
  64. });
  65. describe('createDefaultTrojanClient', () => {
  66. it('produces a Zod-valid client', () => {
  67. const c = createDefaultTrojanClient({ ...seed, password: 'fixed-trojan-pw' });
  68. expect(c).toMatchSnapshot();
  69. expect(TrojanClientSchema.parse(c)).toEqual(c);
  70. });
  71. });
  72. describe('createDefaultShadowsocksClient', () => {
  73. it('produces a Zod-valid client', () => {
  74. const c = createDefaultShadowsocksClient({ ...seed, password: 'ZmFrZS1zcy1wYXNzd29yZA==' });
  75. expect(c).toMatchSnapshot();
  76. expect(ShadowsocksClientSchema.parse(c)).toEqual(c);
  77. });
  78. });
  79. describe('createDefaultHysteriaClient', () => {
  80. it('produces a Zod-valid client', () => {
  81. const c = createDefaultHysteriaClient({ ...seed, auth: 'fixed-hyst-auth' });
  82. expect(c).toMatchSnapshot();
  83. expect(HysteriaClientSchema.parse(c)).toEqual(c);
  84. });
  85. });
  86. describe('createDefaultTuicClient', () => {
  87. it('produces a Zod-valid client', () => {
  88. const c = createDefaultTuicClient({
  89. ...seed,
  90. uuid: '11111111-2222-3333-4444-555555555555',
  91. password: 'fixed-tuic-pw',
  92. });
  93. expect(TuicClientSchema.parse(c)).toEqual(c);
  94. });
  95. });
  96. describe('createDefault*InboundSettings factories', () => {
  97. it('vless', () => {
  98. const s = createDefaultVlessInboundSettings();
  99. expect(s).toMatchSnapshot();
  100. expect(VlessInboundSettingsSchema.parse(s)).toEqual(s);
  101. });
  102. it('vmess', () => {
  103. const s = createDefaultVmessInboundSettings();
  104. expect(s).toMatchSnapshot();
  105. expect(VmessInboundSettingsSchema.parse(s)).toEqual(s);
  106. });
  107. it('trojan', () => {
  108. const s = createDefaultTrojanInboundSettings();
  109. expect(s).toMatchSnapshot();
  110. expect(TrojanInboundSettingsSchema.parse(s)).toEqual(s);
  111. });
  112. it('shadowsocks', () => {
  113. const s = createDefaultShadowsocksInboundSettings({ password: 'ZmFrZS1zcy1zZWVk' });
  114. expect(s).toMatchSnapshot();
  115. expect(ShadowsocksInboundSettingsSchema.parse(s)).toEqual(s);
  116. });
  117. it('hysteria (v1, defaults to v2 wire version)', () => {
  118. const s = createDefaultHysteriaInboundSettings();
  119. expect(s).toMatchSnapshot();
  120. expect(HysteriaInboundSettingsSchema.parse(s)).toEqual(s);
  121. });
  122. it('http', () => {
  123. const s = createDefaultHttpInboundSettings();
  124. expect(s.allowTransparent).toBe(false);
  125. const accounts = s.accounts ?? [];
  126. expect(accounts).toHaveLength(1);
  127. expect(accounts[0].user.length).toBe(8);
  128. expect(accounts[0].pass.length).toBe(12);
  129. expect(HttpInboundSettingsSchema.parse(s)).toEqual(s);
  130. });
  131. it('mixed', () => {
  132. const s = createDefaultMixedInboundSettings();
  133. expect(s.auth).toBe('password');
  134. expect(s.udp).toBe(false);
  135. expect(s.ip).toBe('127.0.0.1');
  136. const accounts = s.accounts ?? [];
  137. expect(accounts).toHaveLength(1);
  138. expect(accounts[0].user.length).toBe(8);
  139. expect(accounts[0].pass.length).toBe(12);
  140. expect(MixedInboundSettingsSchema.parse(s)).toEqual(s);
  141. });
  142. it('tunnel', () => {
  143. const s = createDefaultTunnelInboundSettings();
  144. expect(s).toMatchSnapshot();
  145. expect(TunnelInboundSettingsSchema.parse(s)).toEqual(s);
  146. });
  147. it('wireguard', () => {
  148. const s = createDefaultWireguardInboundSettings({
  149. secretKey: 'QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=',
  150. });
  151. expect(s).toMatchSnapshot();
  152. expect(WireguardInboundSettingsSchema.parse(s)).toEqual(s);
  153. expect(s.peers).toEqual([]);
  154. expect(s.clients).toEqual([]);
  155. });
  156. it('tuic', () => {
  157. const s = createDefaultTuicInboundSettings();
  158. expect(TuicInboundSettingsSchema.parse(s)).toEqual(s);
  159. });
  160. });
  161. describe('TuicInboundSettingsSchema', () => {
  162. it('canonicalizes congestion-controller aliases and casing', () => {
  163. expect(
  164. TuicInboundSettingsSchema.parse({ server: { congestion_control: 'RENO' } }).server
  165. ?.congestion_control,
  166. ).toBe('new_reno');
  167. expect(
  168. TuicInboundSettingsSchema.parse({ server: { congestion_control: ' CuBiC ' } }).server
  169. ?.congestion_control,
  170. ).toBe('cubic');
  171. expect(
  172. TuicInboundSettingsSchema.parse({ server: { congestion_control: '' } }).server
  173. ?.congestion_control,
  174. ).toBe('bbr');
  175. expect(
  176. TuicInboundSettingsSchema.parse({ server: { congestion_control: ' ' } }).server
  177. ?.congestion_control,
  178. ).toBe('bbr');
  179. expect(
  180. TuicInboundSettingsSchema.parse({ congestion_control: ' CuBiC ' }).congestion_control,
  181. ).toBe('cubic');
  182. expect(
  183. TuicInboundSettingsSchema.parse({ congestion_control: 'invalid' }).congestion_control,
  184. ).toBe('new_reno');
  185. });
  186. it('clamps legacy packet-size values to the SOCKS-safe UDP payload maximum', () => {
  187. expect(
  188. TuicInboundSettingsSchema.parse({ server: { max_udp_relay_packet_size: 65507 } }).server
  189. ?.max_udp_relay_packet_size,
  190. ).toBe(65245);
  191. expect(
  192. TuicInboundSettingsSchema.parse({ max_udp_relay_packet_size: 65500 })
  193. .max_udp_relay_packet_size,
  194. ).toBe(65245);
  195. expect(() =>
  196. TuicInboundSettingsSchema.parse({ server: { max_udp_relay_packet_size: 65508 } }),
  197. ).toThrow();
  198. expect(() => TuicInboundSettingsSchema.parse({ max_udp_relay_packet_size: 65508 })).toThrow();
  199. });
  200. });
  201. describe('createHysteriaTlsSettingsWithDefaultCert', () => {
  202. it('defaults Hysteria TLS to uTLS None and h3 ALPN', () => {
  203. const tls = createHysteriaTlsSettingsWithDefaultCert();
  204. expect(tls.alpn).toEqual(['h3']);
  205. expect((tls.settings as Record<string, unknown>).fingerprint).toBe('');
  206. expect(tls.certificates).toEqual([
  207. expect.objectContaining({
  208. useFile: true,
  209. certificateFile: '',
  210. keyFile: '',
  211. }),
  212. ]);
  213. });
  214. });