Files
harbor-net/test/server/gateway-native-config.test.js
T

100 lines
3.5 KiB
JavaScript

import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import test from 'node:test';
const singboxUrl = pathToFileURL(path.resolve('dist/server/singbox.js')).href;
const subscriptionConfig = {
outbounds: [{
type: 'vless',
tag: 'vpn',
server: 'vpn.example.test',
server_port: 443,
uuid: '00000000-0000-4000-8000-000000000000',
tls: { enabled: true },
}],
};
function run(source, { component = 'dataplane', socket = '/tmp/harbor-test.sock' } = {}) {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-gateway-native-config-'));
const script = `
const { buildGatewayConfig, buildDualChannelGatewayConfig } = await import(${JSON.stringify(singboxUrl)});
const subscription = ${JSON.stringify(subscriptionConfig)};
process.stdout.write(JSON.stringify({
single: buildGatewayConfig(subscription, 'vpn'),
dual: buildDualChannelGatewayConfig({
primary: { subscriptionConfig: subscription, selectedServerId: 'vpn' },
reserve: { subscriptionConfig: subscription, selectedServerId: 'vpn' },
}),
}));
`;
try {
return spawnSync(process.execPath, ['--input-type=module', '--eval', script], {
cwd: path.resolve('.'),
encoding: 'utf8',
env: {
...process.env,
APP_MODE: 'gateway',
APP_COMPONENT: component,
DATA_DIR: directory,
SING_BOX_CACHE: path.join(directory, 'cache.db'),
SING_BOX_TRAFFIC_SOURCE: source,
DATAPLANE_SOCKET: socket,
},
});
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}
}
test('Gateway snapshot keeps the 1.13-compatible config and Clash traffic API', () => {
const result = run('snapshot', { component: '', socket: '' });
assert.equal(result.status, 0, result.stderr);
const configs = JSON.parse(result.stdout);
for (const config of Object.values(configs)) {
assert.equal(config.services, undefined);
assert.deepEqual(config.dns, { independent_cache: true });
assert.deepEqual(config.experimental.clash_api, { external_controller: '127.0.0.1:19090' });
}
});
test('Gateway shadow and native add one secret-free loopback API to single and dual configs', () => {
for (const source of ['shadow', 'native']) {
const result = run(source);
assert.equal(result.status, 0, result.stderr);
const configs = JSON.parse(result.stdout);
for (const config of Object.values(configs)) {
assert.deepEqual(config.services, [{
type: 'api',
listen: '127.0.0.1',
listen_port: 19091,
dashboard: false,
}]);
assert.equal(JSON.stringify(config).includes('secret'), false);
assert.deepEqual(config.dns, {});
assert.deepEqual(config.experimental.clash_api, { external_controller: '127.0.0.1:19090' });
}
}
});
test('Gateway shadow and native reject combined or socket-less topology', () => {
for (const options of [
{ component: '', socket: '' },
{ component: 'control', socket: '' },
{ component: 'dataplane', socket: '' },
]) {
const result = run('native', options);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /require split control\/dataplane topology/);
}
});
test('Gateway rejects unknown traffic sources', () => {
const result = run('disabled');
assert.notEqual(result.status, 0);
assert.match(result.stderr, /must be snapshot, shadow or native/);
});