mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-10 06:36:06 +00:00
b07fad0e69
* v3.4.0 * refactor(wireguard): drop removed `workers` field (xray v26.6.22) xray-core v26.6.22 (PR #6287) removed the WireGuard `workers` (num_workers) config field; the engine now relies on wireguard-go's internal worker fallback and no longer reads it. Remove it from the panel so it stops emitting a key xray ignores. Removed from the inbound/outbound/outbound-form WireGuard schemas, both WireGuard forms, the outbound form adapter (both directions) and defaults, the two affected tests, and the `workers` label in all 13 locales. Existing configs that still carry workers are simply dropped on parse — no migration needed since the field had no runtime effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Update version --------- Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
231 lines
7.8 KiB
TypeScript
231 lines
7.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
createDefaultBlackholeOutboundSettings,
|
|
createDefaultDNSOutboundSettings,
|
|
createDefaultFreedomOutboundSettings,
|
|
createDefaultHttpOutboundSettings,
|
|
createDefaultHysteriaOutboundSettings,
|
|
createDefaultLoopbackOutboundSettings,
|
|
createDefaultShadowsocksOutboundSettings,
|
|
createDefaultSocksOutboundSettings,
|
|
createDefaultTrojanOutboundSettings,
|
|
createDefaultVlessOutboundSettings,
|
|
createDefaultVmessOutboundSettings,
|
|
createDefaultWireguardOutboundSettings,
|
|
createDefaultOutboundSettings,
|
|
} from '@/lib/xray/outbound-defaults';
|
|
import {
|
|
BlackholeOutboundSettingsSchema,
|
|
DNSOutboundSettingsSchema,
|
|
FreedomOutboundSettingsSchema,
|
|
HttpOutboundSettingsSchema,
|
|
HysteriaOutboundSettingsSchema,
|
|
LoopbackOutboundSettingsSchema,
|
|
ShadowsocksOutboundSettingsSchema,
|
|
SocksOutboundSettingsSchema,
|
|
TrojanOutboundSettingsSchema,
|
|
VlessOutboundSettingsSchema,
|
|
VmessOutboundSettingsSchema,
|
|
WireguardOutboundSettingsSchema,
|
|
} from '@/schemas/protocols/outbound';
|
|
|
|
// Snapshot + Zod round-trip for each createDefault*OutboundSettings factory.
|
|
// The factory output mirrors the legacy `new Outbound.<X>Settings()` start
|
|
// state, so most required fields are empty stubs (address, port, password,
|
|
// id). Zod parsing happens AFTER patching the stubs with sensible values —
|
|
// this catches schema/factory drift without forcing the factory to invent
|
|
// data it shouldn't.
|
|
|
|
const SAMPLE_ID = '11111111-2222-4333-8444-555555555555';
|
|
const SAMPLE_ADDRESS = '1.2.3.4';
|
|
const SAMPLE_PORT = 443;
|
|
const SAMPLE_SECRET = 'abc123def456ghi789';
|
|
|
|
describe('outbound default factories: shape snapshots', () => {
|
|
it('freedom is the empty object', () => {
|
|
expect(createDefaultFreedomOutboundSettings()).toEqual({});
|
|
});
|
|
|
|
it('blackhole is the empty object', () => {
|
|
expect(createDefaultBlackholeOutboundSettings()).toEqual({});
|
|
});
|
|
|
|
it('loopback has an empty inboundTag', () => {
|
|
expect(createDefaultLoopbackOutboundSettings()).toEqual({ inboundTag: '' });
|
|
});
|
|
|
|
it('dns has the legacy constructor defaults', () => {
|
|
expect(createDefaultDNSOutboundSettings()).toEqual({
|
|
rewriteNetwork: '',
|
|
rewriteAddress: '',
|
|
rewritePort: 53,
|
|
userLevel: 0,
|
|
rules: [],
|
|
});
|
|
});
|
|
|
|
it('vmess wraps a single vnext server with one user', () => {
|
|
expect(createDefaultVmessOutboundSettings()).toEqual({
|
|
vnext: [{ address: '', port: 443, users: [{ id: '', security: 'auto' }] }],
|
|
});
|
|
});
|
|
|
|
it('vless lays the connect target flat', () => {
|
|
expect(createDefaultVlessOutboundSettings()).toEqual({
|
|
address: '',
|
|
port: 443,
|
|
id: '',
|
|
flow: '',
|
|
encryption: 'none',
|
|
});
|
|
});
|
|
|
|
it('trojan wraps a single server', () => {
|
|
expect(createDefaultTrojanOutboundSettings()).toEqual({
|
|
servers: [{ address: '', port: 443, password: '' }],
|
|
});
|
|
});
|
|
|
|
it('shadowsocks defaults to 2022-blake3-aes-128-gcm', () => {
|
|
expect(createDefaultShadowsocksOutboundSettings()).toEqual({
|
|
servers: [{
|
|
address: '', port: 443, password: '', method: '2022-blake3-aes-128-gcm',
|
|
}],
|
|
});
|
|
});
|
|
|
|
it('socks defaults to port 1080 with no users', () => {
|
|
expect(createDefaultSocksOutboundSettings()).toEqual({
|
|
servers: [{ address: '', port: 1080, users: [] }],
|
|
});
|
|
});
|
|
|
|
it('http defaults to port 8080 with no users', () => {
|
|
expect(createDefaultHttpOutboundSettings()).toEqual({
|
|
servers: [{ address: '', port: 8080, users: [] }],
|
|
});
|
|
});
|
|
|
|
it('wireguard seeds secretKey deterministically when given', () => {
|
|
const out = createDefaultWireguardOutboundSettings({ secretKey: SAMPLE_SECRET });
|
|
expect(out.secretKey).toBe(SAMPLE_SECRET);
|
|
expect(out.mtu).toBe(1420);
|
|
expect(out.address).toEqual([]);
|
|
expect(out.noKernelTun).toBe(false);
|
|
expect(out.peers).toEqual([{
|
|
publicKey: '', allowedIPs: ['0.0.0.0/0', '::/0'], endpoint: '',
|
|
}]);
|
|
});
|
|
|
|
it('wireguard generates a secretKey when none is seeded', () => {
|
|
const out = createDefaultWireguardOutboundSettings();
|
|
expect(out.secretKey).toMatch(/^[A-Za-z0-9+/=]+$/);
|
|
expect(out.secretKey.length).toBeGreaterThan(8);
|
|
});
|
|
|
|
it('hysteria defaults to port 443 version 2', () => {
|
|
expect(createDefaultHysteriaOutboundSettings()).toEqual({
|
|
address: '', port: 443, version: 2,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('outbound default factories: schema acceptance after stub fill-in', () => {
|
|
it('freedom default parses (no required fields)', () => {
|
|
expect(FreedomOutboundSettingsSchema.safeParse(
|
|
createDefaultFreedomOutboundSettings(),
|
|
).success).toBe(true);
|
|
});
|
|
|
|
it('blackhole default parses (no required fields)', () => {
|
|
expect(BlackholeOutboundSettingsSchema.safeParse(
|
|
createDefaultBlackholeOutboundSettings(),
|
|
).success).toBe(true);
|
|
});
|
|
|
|
it('loopback default parses (no required fields)', () => {
|
|
expect(LoopbackOutboundSettingsSchema.safeParse(
|
|
createDefaultLoopbackOutboundSettings(),
|
|
).success).toBe(true);
|
|
});
|
|
|
|
it('dns default parses', () => {
|
|
expect(DNSOutboundSettingsSchema.safeParse(
|
|
createDefaultDNSOutboundSettings(),
|
|
).success).toBe(true);
|
|
});
|
|
|
|
it('vmess parses once vnext fields are filled', () => {
|
|
const def = createDefaultVmessOutboundSettings();
|
|
def.vnext[0].address = SAMPLE_ADDRESS;
|
|
def.vnext[0].port = SAMPLE_PORT;
|
|
def.vnext[0].users[0].id = SAMPLE_ID;
|
|
expect(VmessOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('vless parses once address/port/id are filled', () => {
|
|
const def = createDefaultVlessOutboundSettings();
|
|
def.address = SAMPLE_ADDRESS;
|
|
def.port = SAMPLE_PORT;
|
|
def.id = SAMPLE_ID;
|
|
expect(VlessOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('trojan parses once server fields are filled', () => {
|
|
const def = createDefaultTrojanOutboundSettings();
|
|
def.servers[0].address = SAMPLE_ADDRESS;
|
|
def.servers[0].password = 'secret';
|
|
expect(TrojanOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('shadowsocks parses once server fields are filled', () => {
|
|
const def = createDefaultShadowsocksOutboundSettings();
|
|
def.servers[0].address = SAMPLE_ADDRESS;
|
|
def.servers[0].password = 'secret';
|
|
expect(ShadowsocksOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('socks parses once address is filled', () => {
|
|
const def = createDefaultSocksOutboundSettings();
|
|
def.servers[0].address = SAMPLE_ADDRESS;
|
|
expect(SocksOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('http parses once address is filled', () => {
|
|
const def = createDefaultHttpOutboundSettings();
|
|
def.servers[0].address = SAMPLE_ADDRESS;
|
|
expect(HttpOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('wireguard parses once peer + secretKey are filled', () => {
|
|
const def = createDefaultWireguardOutboundSettings({ secretKey: SAMPLE_SECRET });
|
|
def.peers[0].publicKey = 'pk';
|
|
def.peers[0].endpoint = `${SAMPLE_ADDRESS}:51820`;
|
|
expect(WireguardOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
|
|
it('hysteria parses once address is filled', () => {
|
|
const def = createDefaultHysteriaOutboundSettings();
|
|
def.address = SAMPLE_ADDRESS;
|
|
expect(HysteriaOutboundSettingsSchema.safeParse(def).success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('createDefaultOutboundSettings dispatcher', () => {
|
|
const PROTOCOLS = [
|
|
'freedom', 'blackhole', 'dns', 'vmess', 'vless', 'trojan', 'shadowsocks',
|
|
'socks', 'http', 'wireguard', 'hysteria', 'loopback',
|
|
];
|
|
|
|
for (const protocol of PROTOCOLS) {
|
|
it(`returns non-null for ${protocol}`, () => {
|
|
expect(createDefaultOutboundSettings(protocol)).not.toBeNull();
|
|
});
|
|
}
|
|
|
|
it('returns null for an unknown protocol', () => {
|
|
expect(createDefaultOutboundSettings('mysterious')).toBeNull();
|
|
});
|
|
});
|