80 lines
3.1 KiB
JavaScript
80 lines
3.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
import {
|
|
checkServerHealth,
|
|
createServerHealthService,
|
|
} from '../../dist/server/features/servers/index.js';
|
|
import { createServerHealthRoute } from '../../dist/server/http/routes/serverHealthRoute.js';
|
|
|
|
test('server health checks cap count and concurrency', async () => {
|
|
const servers = Array.from({ length: 300 }, (_, index) => ({
|
|
id: `srv-${index}`,
|
|
label: `Server ${index}`,
|
|
host: `server-${index}.example`,
|
|
port: 443,
|
|
}));
|
|
let active = 0;
|
|
let peak = 0;
|
|
const results = await checkServerHealth(servers, async () => {
|
|
active += 1;
|
|
peak = Math.max(peak, active);
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
active -= 1;
|
|
return { ok: true, latency: 1 };
|
|
});
|
|
|
|
assert.equal(results.length, 30);
|
|
assert.equal(peak, 4);
|
|
assert.deepEqual(results.map(({ id }) => id), servers.slice(0, 30).map(({ id }) => id));
|
|
});
|
|
|
|
test('server health selection coerces IDs, deduplicates, ignores unknowns, and keeps canonical order', async () => {
|
|
const servers = [
|
|
{ id: '1', label: 'One', host: 'one.example', port: 1, protocol: 'vless' },
|
|
{ id: '2', label: 'Two', host: 'two.example', port: 2, protocol: 'vless' },
|
|
{ id: '3', label: 'Three', host: 'three.example', port: 3, protocol: 'vless' },
|
|
];
|
|
const checked = [];
|
|
const service = createServerHealthService({
|
|
readServers: () => servers,
|
|
ping: async (host, port) => {
|
|
checked.push([host, port]);
|
|
return { ok: true };
|
|
},
|
|
});
|
|
|
|
assert.deepEqual((await service.check(['3', 1, '3', 'missing'])).map(({ id }) => id), ['1', '3']);
|
|
assert.deepEqual(checked, [['one.example', 1], ['three.example', 3]]);
|
|
assert.deepEqual((await service.check(undefined)).map(({ id }) => id), ['1', '2', '3']);
|
|
assert.deepEqual((await service.check('2')).map(({ id }) => id), ['1', '2', '3']);
|
|
assert.deepEqual((await service.check([])).map(({ id }) => id), ['1', '2', '3']);
|
|
assert.deepEqual(await service.check(['missing']), []);
|
|
|
|
const failure = new Error('ping failed');
|
|
await assert.rejects(
|
|
createServerHealthService({ readServers: () => servers, ping: async () => { throw failure; } }).check([]),
|
|
(error) => error === failure,
|
|
);
|
|
});
|
|
|
|
test('server health route is the only endpoint adapter', async () => {
|
|
const sent = [];
|
|
const route = createServerHealthRoute({
|
|
serverHealth: { check: async (ids) => [{ ids }] },
|
|
readBody: async () => ({ serverIds: ['chosen'] }),
|
|
sendState: async (_res, extra) => { sent.push(extra); },
|
|
});
|
|
const response = {};
|
|
|
|
assert.equal(await route.handle({ method: 'GET', url: '/api/servers/ping-all' }, response), false);
|
|
assert.equal(await route.handle({ method: 'POST', url: '/api/servers/ping-all' }, response), true);
|
|
assert.deepEqual(sent, [{ results: [{ ids: ['chosen'] }] }]);
|
|
|
|
const source = readFileSync(new URL('../../src/server/index.ts', import.meta.url), 'utf8');
|
|
assert.match(source, /createServerHealthRoute\(\{/);
|
|
assert.doesNotMatch(source, /req\.url === ['"]\/api\/servers\/ping-all['"]/);
|
|
});
|