Files
Cs2-Mapveto/server/test/liveScore.test.js
T
mregosar 5886461a9e Echte Logdatei durch anonymisierte Fassung ersetzen
Die Testdatei enthielt Spielernamen und SteamID eines echten Accounts.
Sie ist jetzt durch eine Kopie mit Platzhaltern ersetzt, der Parser-Test
prüft weiterhin echte CS2-Logzeilen.
2026-10-29 07:38:38 +01:00

135 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { applyLogLines, initialLiveState, seriesScore, splitLogBody } from '../src/liveScore.js';
const here = path.dirname(fileURLToPath(import.meta.url));
const ctx = { teamNames: { A: 'MaxLAN Blau', B: 'MaxLAN Rot' }, maxRounds: 24, overtimeRounds: 6 };
const ts = '10/25/2026 - 18:00:00.000 - ';
function round(winnerSide, ct, t, notice) {
const n = notice ?? (winnerSide === 'CT' ? 'CTs_Win' : 'Terrorists_Win');
const side = winnerSide === 'CT' ? 'CT' : 'TERRORIST';
return `${ts}Team "${side}" triggered "SFUI_Notice_${n}" (CT "${ct}") (T "${t}")`;
}
test('Logzeilen ergeben Spielstand, Rundenverlauf und Gründe', () => {
let state = initialLiveState({ map: 'de_mirage', ctTeam: 'A', warmupEnded: true });
const lines = [
`${ts}World triggered "Match_Start" on "de_mirage"`,
round('T', 0, 1, 'Target_Bombed'),
round('CT', 1, 1, 'Bomb_Defused'),
round('CT', 2, 1, 'Target_Saved'),
];
const { state: next, events } = applyLogLines(state, lines, ctx);
state = next;
assert.equal(state.status, 'live');
assert.deepEqual(state.score, { A: 2, B: 1 });
assert.deepEqual(state.rounds.map((r) => [r.winner, r.reason]), [['B', 'bomb'], ['A', 'defuse'], ['A', 'time']]);
assert.equal(events.filter((e) => e.type === 'round').length, 3);
});
test('Doppelt gesendete Zeilen werden nur einmal gezählt', () => {
const state = initialLiveState({ map: 'de_nuke', ctTeam: 'A', warmupEnded: true });
const line = round('CT', 1, 0);
const { state: s } = applyLogLines(state, [line, line, line], ctx);
assert.equal(s.rounds.length, 1);
assert.deepEqual(s.score, { A: 1, B: 0 });
});
test('Seitenzuordnung nach dem Rundenende hat Vorrang, veraltete Zeilen zu Rundenbeginn zählen nicht', () => {
let s = initialLiveState({ map: 'de_nuke', ctTeam: 'B', warmupEnded: true });
const playing = (ct, t) => [`${ts}MatchStatus: Team playing "CT": ${ct}`, `${ts}MatchStatus: Team playing "TERRORIST": ${t}`];
({ state: s } = applyLogLines(s, [
// Veto sagt Rot auf CT, tatsächlich steht Blau auf CT – das Log gewinnt
`${ts}World triggered "Match_Start" on "de_nuke"`,
...playing('MaxLAN Blau', 'MaxLAN Rot'),
round('CT', 1, 0),
...playing('MaxLAN Blau', 'MaxLAN Rot'),
`${ts}World triggered "Round_End"`,
// veraltete Zeile zu Beginn der nächsten Runde (darf nichts ändern)
...playing('MaxLAN Rot', 'MaxLAN Blau'),
round('T', 1, 1),
...playing('MaxLAN Blau', 'MaxLAN Rot'),
`${ts}World triggered "Round_End"`,
], ctx));
assert.deepEqual(s.score, { A: 1, B: 1 });
assert.deepEqual(s.rounds.map((r) => r.winner), ['A', 'B']);
});
test('Echte, anonymisierte Logzeilen eines CS2-Servers (Wingman, 4 Runden, Halbzeit nach 2)', () => {
const lines = fs.readFileSync(path.join(here, 'fixtures', 'wingman-anonym.log'), 'utf8').split(/\r?\n/).filter(Boolean);
const warmupEnd = lines.findIndex((l) => l.includes('command "mp_warmup_end"'));
const ctx = { teamNames: { A: 'MaxLAN Blau', B: 'MaxLAN Rot' }, maxRounds: 4, overtimeRounds: 6 };
const check = (s) => {
assert.equal(s.status, 'finished');
assert.equal(s.winner, 'A');
assert.deepEqual(s.score, { A: 3, B: 1 });
assert.deepEqual(s.rounds.map((r) => r.winner), ['B', 'A', 'A', 'A']);
assert.ok(s.rounds.every((r) => r.reason === 'elimination'));
};
// a) wie vom Server in Paketen geschickt
let s = initialLiveState({ map: 'de_inferno', ctTeam: 'A' });
({ state: s } = applyLogLines(s, lines.slice(0, warmupEnd), ctx));
assert.equal(s.status, 'warmup');
s.warmupEnded = true; // Organisator hat "Aufwärmphase beenden" geklickt
({ state: s } = applyLogLines(s, lines.slice(warmupEnd), ctx));
check(s);
// b) jede Zeile einzeln (ungünstigste Paketgrenzen)
let t = initialLiveState({ map: 'de_inferno', ctTeam: 'A', warmupEnded: true });
for (const line of lines.slice(warmupEnd)) ({ state: t } = applyLogLines(t, [line], ctx));
check(t);
});
test('Ohne "Team playing": Halbzeitwechsel nach Regelwerk', () => {
let s = initialLiveState({ map: 'de_inferno', ctTeam: 'B', warmupEnded: true });
const small = { ...ctx, maxRounds: 4 };
({ state: s } = applyLogLines(s, [
`${ts}World triggered "Match_Start" on "de_inferno"`,
round('CT', 1, 0), // B gewinnt als CT
round('CT', 2, 0), // B gewinnt als CT -> Halbzeit, B ist jetzt T
round('T', 0, 3), // B gewinnt als T: CT-Seite (A) hat 0, T-Seite (B) hat 3
], small));
assert.deepEqual(s.score, { A: 0, B: 3 });
assert.deepEqual(s.rounds.map((r) => r.winner), ['B', 'B', 'B']);
});
test('Game Over beendet die Map, Aufwärmrunden vor Match_Start zählen nicht', () => {
let s = initialLiveState({ map: 'de_dust2', ctTeam: 'A', warmupEnded: true });
({ state: s } = applyLogLines(s, [
round('T', 0, 1), // Aufwärm-/Messerrunde
`${ts}World triggered "Match_Start" on "de_dust2"`,
round('CT', 1, 0),
`${ts}Game Over: competitive mg_active de_dust2 score 1:0 after 2 min`,
round('T', 1, 1), // nach Spielende ignoriert
], ctx));
assert.equal(s.status, 'finished');
assert.equal(s.winner, 'A');
assert.deepEqual(s.score, { A: 1, B: 0 });
});
test('Zeilen einer anderen Map werden ignoriert, Serienstand wird gezählt', () => {
const s = initialLiveState({ map: 'de_dust2', ctTeam: 'A', warmupEnded: true });
const { state } = applyLogLines(s, [`${ts}Game Over: competitive mg_active de_nuke score 13:2 after 30 min`], ctx);
assert.equal(state.status, 'warmup');
assert.deepEqual(seriesScore([{ winner: 'A' }, { winner: 'B' }, { winner: 'A' }, { winner: null }]), { A: 2, B: 1 });
assert.deepEqual(splitLogBody('a\r\n\r\nb\n'), ['a', 'b']);
});
test('Match_Start vor dem Ende der Aufwärmphase startet keine Zählung', () => {
let s = initialLiveState({ map: 'de_inferno', ctTeam: 'A' });
const start = `${ts}World triggered "Match_Start" on "de_inferno"`;
({ state: s } = applyLogLines(s, [start], ctx));
assert.equal(s.status, 'warmup');
s.warmupEnded = true;
const { state, events } = applyLogLines(s, [start], ctx);
assert.equal(state.status, 'live');
assert.equal(events[0].type, 'match_start');
});