import { describe, expect, it } from 'vitest'; import { parseInfoLine, parsePlayerCount, parsePlayerRoster } from '../../src/lib/server/integrations/rcon.js'; describe('parsePlayerCount', () => { it('counts rows after --- --- separator (status dialect)', () => { const body = [ 'print', 'map: mp_kino', 'num score ping guid name', '--- ----- ---- -------- ----', ' 0 12 45 abc123def456 joe', ' 1 0 72 xyz789abc123 jill', ' 2 999 30 111222333444 kade' ].join('\n'); expect(parsePlayerCount(body)).toBe(3); }); it('returns 0 when the scoreboard has no rows', () => { const body = [ 'print', 'map: mp_ascension', 'num score ping guid name', '--- ----- ---- -------- ----', '' ].join('\n'); expect(parsePlayerCount(body)).toBe(0); }); it('counts non-blank lines after the info line (statusResponse dialect)', () => { const body = [ 'statusResponse', '\\mapname\\mp_kino\\g_gametype\\zom\\sv_maxclients\\4', '10 45 "joe"', '0 72 "jill"' ].join('\n'); expect(parsePlayerCount(body)).toBe(2); }); it('returns null on malformed payloads with no separator or info line', () => { expect(parsePlayerCount('nonsense body\nno structure here')).toBeNull(); }); }); describe('parseInfoLine', () => { it('splits \\key\\value pairs from an info string', () => { const body = [ 'statusResponse', '\\mapname\\mp_kino\\g_gametype\\zom\\sv_maxclients\\4' ].join('\n'); const info = parseInfoLine(body); expect(info.mapname).toBe('mp_kino'); expect(info.g_gametype).toBe('zom'); expect(info.sv_maxclients).toBe('4'); }); it('returns empty object when no info line is present', () => { expect(parseInfoLine('nothing here')).toEqual({}); }); }); describe('parsePlayerRoster', () => { it('extracts num/score/ping/guid/name from print dialect', () => { const body = [ 'print', 'map: mp_kino', 'num score ping guid name', '--- ----- ---- -------- ----', ' 0 12 45 aaaaaaaabbbbbbbbccccccccdddddddd joe', ' 1 0 72 11112222333344445555666677778888 jill h.', ' 2 999 128 abcdef0123456789abcdef0123456789 Toaster' ].join('\n'); const roster = parsePlayerRoster(body); expect(roster).toHaveLength(3); expect(roster[0]).toEqual({ num: 0, score: 12, ping: 45, guid: 'aaaaaaaabbbbbbbbccccccccdddddddd', name: 'joe' }); // Names with spaces stay intact expect(roster[1].name).toBe('jill h.'); expect(roster[2].ping).toBe(128); }); it('extracts score/ping/quoted-name from statusResponse dialect', () => { const body = [ 'statusResponse', '\\mapname\\mp_kino\\g_gametype\\zclassic', '10 45 "joe"', '0 72 "jill h."' ].join('\n'); const roster = parsePlayerRoster(body); expect(roster).toHaveLength(2); expect(roster[0]).toEqual({ num: null, score: 10, ping: 45, guid: null, name: 'joe' }); expect(roster[1].name).toBe('jill h.'); }); it('returns empty array when no rows are present', () => { const empty = [ 'print', 'map: mp_ascension', 'num score ping guid name', '--- ----- ---- -------- ----' ].join('\n'); expect(parsePlayerRoster(empty)).toEqual([]); expect(parsePlayerRoster('nonsense')).toEqual([]); }); });