Files
harbor-net/test/server/singbox-gateway-mode.test.js
T
dokril daec12e013
Build and Deploy Gateway / build-and-push (push) Failing after 14s
Build and Deploy Gateway / deploy (push) Has been skipped
Update Harbor client and gateway integration workflows
2026-08-19 18:16:10 +03:00

127 lines
5.2 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
process.env.APP_MODE = 'gateway';
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'vpn-proxy-gateway-test-'));
process.env.SING_BOX_CACHE = path.join(process.env.DATA_DIR, 'cache.db');
const {
buildDualChannelGatewayConfig,
buildGatewayConfig,
dualChannelConfigMatchesApplied,
fingerprintConfiguredOutbound,
fingerprintSelectedOutbound,
} = await import(`../../dist/server/singbox.js?gateway=${Date.now()}`);
const subscriptionConfig = {
outbounds: [{
type: 'vless',
tag: 'test-vpn',
server: 'vpn.example.test',
server_port: 443,
uuid: '00000000-0000-4000-8000-000000000000',
tls: { enabled: true },
}],
};
test('gateway preserves mixed user-rule order and dynamic VPN target', () => {
const config = buildGatewayConfig(subscriptionConfig, 'test-vpn', {
routeRules: [
{ type: 'domain', value: 'api.example.com', enabled: true, outbound: 'vpn' },
{ type: 'domain_suffix', value: 'example.com', enabled: true, outbound: 'direct' },
{ type: 'domain_keyword', value: 'disabled', enabled: false, outbound: 'vpn' },
],
});
assert.deepEqual(config.route.rule_set, []);
assert.deepEqual(config.inbounds.map((inbound) => inbound.tag), [
'tproxy-in',
'mixed-in',
'diagnostics-vpn-in',
]);
assert.deepEqual(config.inbounds[2], {
type: 'mixed',
tag: 'diagnostics-vpn-in',
listen: '127.0.0.1',
listen_port: 18080,
set_system_proxy: false,
});
assert.deepEqual(config.route.rules, [
{
inbound: ['tproxy-in', 'mixed-in', 'diagnostics-vpn-in'],
action: 'sniff',
sniffer: ['http', 'tls', 'quic'],
timeout: '1s',
},
{ inbound: ['diagnostics-vpn-in'], outbound: 'test-vpn' },
{ domain: ['api.example.com'], outbound: 'test-vpn' },
{ domain_suffix: ['example.com'], outbound: 'direct' },
{ inbound: ['tproxy-in'], outbound: 'test-vpn' },
{ inbound: ['mixed-in'], outbound: 'test-vpn' },
]);
assert.deepEqual(config.experimental.clash_api, { external_controller: '127.0.0.1:19090' });
assert.equal(config.route.final, 'test-vpn');
});
test('gateway dual-channel config fixes probes to role tags and keeps inbound connections', () => {
const reserveConfig = structuredClone(subscriptionConfig);
reserveConfig.outbounds[0].tag = 'same-provider-tag';
const primaryConfig = structuredClone(subscriptionConfig);
primaryConfig.outbounds[0].tag = 'same-provider-tag';
const config = buildDualChannelGatewayConfig({
primary: { subscriptionConfig: primaryConfig, selectedServerId: 'same-provider-tag' },
reserve: { subscriptionConfig: reserveConfig, selectedServerId: 'same-provider-tag' },
}, {
routeRules: [{ type: 'domain', value: 'api.example.com', enabled: true, outbound: 'vpn' }],
});
assert.deepEqual(config.outbounds.map(({ tag }) => tag), [
'channel-primary', 'channel-reserve', 'channel-selector', 'direct',
]);
assert.deepEqual(config.outbounds[2], {
type: 'selector',
tag: 'channel-selector',
outbounds: ['channel-primary', 'channel-reserve'],
default: 'channel-primary',
interrupt_exist_connections: false,
});
assert.deepEqual(config.route.rules.slice(1, 5), [
{ inbound: ['diagnostics-primary-in'], outbound: 'channel-primary' },
{ inbound: ['diagnostics-reserve-in'], outbound: 'channel-reserve' },
{ inbound: ['diagnostics-vpn-in'], outbound: 'channel-selector' },
{ domain: ['api.example.com'], outbound: 'channel-selector' },
]);
assert.equal(config.route.final, 'channel-selector');
const restoredReserve = buildDualChannelGatewayConfig({
primary: { subscriptionConfig: primaryConfig, selectedServerId: 'same-provider-tag' },
reserve: { subscriptionConfig: reserveConfig, selectedServerId: 'same-provider-tag' },
}, { defaultRole: 'reserve' });
assert.equal(restoredReserve.outbounds[2].default, 'channel-reserve');
});
test('cached dual-channel outbounds must match the applied provider fingerprints', () => {
const config = buildDualChannelGatewayConfig({
primary: { subscriptionConfig, selectedServerId: 'test-vpn' },
reserve: { subscriptionConfig, selectedServerId: 'test-vpn' },
});
const expected = fingerprintSelectedOutbound(subscriptionConfig, 'test-vpn');
const applied = {
primary: { profileId: 'primary', serverId: 'test-vpn' },
reserve: { profileId: 'reserve', serverId: 'test-vpn' },
primaryConfigFingerprint: expected,
reserveConfigFingerprint: expected,
};
assert.equal(dualChannelConfigMatchesApplied(config, applied, 'primary'), true);
assert.equal(dualChannelConfigMatchesApplied(config, applied, 'reserve'), false);
assert.equal(fingerprintConfiguredOutbound(config.outbounds[0], 'test-vpn'), expected);
config.outbounds[0].uuid = '11111111-1111-4111-8111-111111111111';
assert.notEqual(fingerprintConfiguredOutbound(config.outbounds[0], 'test-vpn'), expected);
assert.equal(dualChannelConfigMatchesApplied(config, applied, 'primary'), false);
config.outbounds[0].uuid = subscriptionConfig.outbounds[0].uuid;
config.outbounds[2].outbounds = ['channel-primary'];
assert.equal(dualChannelConfigMatchesApplied(config, applied, 'primary'), false);
});