Files
Cs2-Mapveto/server/test/lanPorts.test.js

96 lines
4.8 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { formatPortList, parsePortList } from '../src/config.js';
import { countSlots, DockerHostPool, planPorts } from '../src/servers/dockerAdapter.js';
const host = { portRangeStart: 27100, portRangeEnd: 27105, lanPorts: [27015, 27016] };
const secrets = { rconPassword: 'Rcon123', serverPassword: 'Pw123', tvPassword: 'Tv123' };
test('Portlisten werden gelesen und kompakt geschrieben', () => {
assert.deepEqual(parsePortList('27015-27017, 27020'), [27015, 27016, 27017, 27020]);
assert.deepEqual(parsePortList(''), []);
assert.throws(() => parsePortList('abc'), /kein Port/);
assert.throws(() => parsePortList('80-90'), /ungültig/);
assert.equal(formatPortList([27020, 27015, 27016, 27017]), '27015-27017,27020');
});
test('Spielport kommt zuerst aus den LAN-Ports, CSTV liegt immer 100 darüber', () => {
assert.deepEqual(planPorts(host, new Set()), { port: 27015, tvPort: 27115 });
assert.deepEqual(planPorts(host, new Set([27015, 27115])), { port: 27016, tvPort: 27116 });
// LAN-Ports voll: weiter im normalen Bereich, CSTV ebenfalls +100
assert.deepEqual(planPorts(host, new Set([27015, 27016, 27115, 27116])), { port: 27100, tvPort: 27200 });
// Spielport frei, aber sein CSTV-Port belegt: nächster Spielport
assert.deepEqual(planPorts({ ...host, lanPorts: [] }, new Set([27200])), { port: 27101, tvPort: 27201 });
assert.equal(planPorts({ ...host, lanPorts: [] }, new Set([27100, 27101, 27102, 27103, 27104, 27105])), null);
});
test('Abstand zwischen Spiel- und CSTV-Port ist einstellbar', () => {
assert.deepEqual(planPorts({ ...host, tvPortOffset: 5 }, new Set()), { port: 27015, tvPort: 27020 });
});
test('Plätze pro Host: 2 LAN-Ports plus 6 Spielports im normalen Bereich', () => {
assert.equal(countSlots(host, new Set()), 8);
assert.equal(countSlots({ ...host, lanPorts: [] }, new Set()), 6);
});
function fakePool({ busy = [] } = {}) {
const calls = [];
const runDocker = async (args) => {
calls.push(args);
if (args[0] === 'run') {
const ports = args.filter((a, i) => args[i - 1] === '-p').map((a) => Number(a.split(':')[0]));
const portEnv = args.find((a) => a.startsWith('CS2_PORT='));
if (args.includes('--network') && portEnv) ports.push(Number(portEnv.split('=')[1]));
if (ports.some((p) => busy.includes(p))) {
const err = new Error('Command failed');
err.stderr = 'Bind for 0.0.0.0:27015 failed: port is already allocated';
throw err;
}
}
if (args[0] === 'inspect') return 'running';
return '';
};
const p = new DockerHostPool({
bin: 'docker', image: 'joedwards32/cs2:latest', host: '10.0.0.9', rconHost: '10.0.0.9',
portRangeStart: 27100, portRangeEnd: 27139, gslt: '', volume: 'v', extraArgs: '',
startTimeoutMinutes: 1, lanPorts: [27015, 27016], serverNamePrefix: 'MaxLAN',
hosts: [{ name: 'a', publicHost: '10.0.0.1' }],
}, { runDocker });
for (const a of p.adapters.values()) a.waitForUpdateCheck = async () => {};
return { pool: p, calls };
}
test('Servername zeigt die Teams, Spielport ist ein LAN-Port', async () => {
const { pool: p, calls } = fakePool();
const r = await p.start({ matchId: 'm1', mode: 'wingman', secrets, teamNames: { A: 'Rote "Füchse"', B: 'Blau' } });
assert.equal(r.port, 27015);
assert.equal(r.tvPort, 27115);
const run = calls.find((c) => c[0] === 'run');
assert.ok(run.includes('CS2_SERVERNAME=MaxLAN | Rote Füchse vs. Blau'));
// Auf LAN-Ports im Host-Netz, damit Broadcasts des Serverbrowsers ankommen; keine Port-Weiterleitung
assert.equal(run[run.indexOf('--network') + 1], 'host');
assert.ok(!run.includes('-p'));
});
test('Server außerhalb der LAN-Ports laufen weiter mit Port-Weiterleitung', async () => {
const { pool: p, calls } = fakePool();
await p.start({ matchId: 'x1', mode: 'wingman', secrets, teamNames: { A: 'A', B: 'B' } });
await p.start({ matchId: 'x2', mode: 'wingman', secrets, teamNames: { A: 'A', B: 'B' } });
calls.length = 0;
// Die App kennt ihre Ports aus der Datenbank; hier sind beide LAN-Ports belegt
const adapter = p.adapters.get('a');
adapter.getUsedPorts = () => new Set([27015, 27016, 27115, 27116]);
await adapter.launch({ matchId: 'x3', mode: 'wingman', secrets });
const run = calls.find((c) => c[0] === 'run');
assert.ok(!run.includes('--network'));
assert.ok(run.includes('27100:27100/udp'));
assert.ok(run.includes('27200:27200/udp'));
});
test('Ist ein LAN-Port von einem fremden Dienst belegt, nimmt der Start den nächsten', async () => {
const { pool: p, calls } = fakePool({ busy: [27015] });
const r = await p.start({ matchId: 'm2', mode: 'competitive', secrets, teamNames: { A: 'A', B: 'B' } });
assert.equal(r.port, 27016);
assert.equal(calls.filter((c) => c[0] === 'run').length, 2);
});