100 lines
3.5 KiB
JavaScript
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/);
|
|
});
|