Add Prettier and ESLint tooling (Refs #77)

This commit is contained in:
2026-10-09 15:56:13 -04:00
parent 8ed059b8d3
commit 16469e2d43
711 changed files with 99737 additions and 28286 deletions
+329 -95
View File
@@ -1,14 +1,31 @@
import { describe, expect, it } from 'vitest';
import { freshTelemetry, selectServerObservation, type ServerObservation } from '../../src/lib/server-observation.js';
import {
freshTelemetry,
selectServerObservation,
type ServerObservation
} from '../../src/lib/server-observation.js';
import { gameEvent } from '../helpers/telemetry.js';
const now = Date.parse('2026-09-27T12:00:00Z');
const at = (offset = 0) => new Date(now + offset).toISOString();
const report = (offset = 0, players = 3) => ({ ...gameEvent('heartbeat', 15, players, 'managed'), receivedAt: at(offset) });
const report = (offset = 0, players = 3) => ({
...gameEvent('heartbeat', 15, players, 'managed'),
receivedAt: at(offset)
});
function server(changes: Partial<ServerObservation> = {}): ServerObservation {
return { name: 'managed', health: 'online', container_state: 'running', container_checked_at: at(),
last_players: 4, last_map: 'zombie_theater', last_gametype: 'zom', last_seen_at: at(),
last_error: null, roster: [], ...changes };
return {
name: 'managed',
health: 'online',
container_state: 'running',
container_checked_at: at(),
last_players: 4,
last_map: 'zombie_theater',
last_gametype: 'zom',
last_seen_at: at(),
last_error: null,
roster: [],
...changes
};
}
describe('shared server observation', () => {
@@ -16,22 +33,52 @@ describe('shared server observation', () => {
for (const reason of ['timeout', 'send-error']) {
const raw = { ...server(), last_error: `${reason}:no reply`, rcon_failures: 1 };
const selected = selectServerObservation(server({ rcon_status: raw }), undefined, now);
expect(selected).toMatchObject({ health: 'online', last_error: null, rcon_status: raw });
expect(selectServerObservation(selected, undefined, now + 90_000).health).toBe('online');
expect(selected).toMatchObject({
health: 'online',
last_error: null,
rcon_status: raw
});
expect(selectServerObservation(selected, undefined, now + 90_000).health).toBe(
'online'
);
expect(selectServerObservation(selected, undefined, now + 90_001).health).toBe('stale');
expect(selectServerObservation(selected, undefined, now + 120_001).health).toBe('offline');
expect(selectServerObservation(server({ rcon_status: { ...raw, rcon_failures: 2 } }), undefined, now))
.toMatchObject({ health: 'stale', last_error: raw.last_error });
expect(selectServerObservation(selected, undefined, now + 120_001).health).toBe(
'offline'
);
expect(
selectServerObservation(
server({ rcon_status: { ...raw, rcon_failures: 2 } }),
undefined,
now
)
).toMatchObject({ health: 'stale', last_error: raw.last_error });
}
for (const reason of ['no-config', 'no-port', 'bad-reply', 'no-players']) {
expect(selectServerObservation(server({ last_error: `${reason}:failed`, rcon_failures: 1 }), undefined, now).health).toBe('stale');
expect(
selectServerObservation(
server({ last_error: `${reason}:failed`, rcon_failures: 1 }),
undefined,
now
).health
).toBe('stale');
}
expect(selectServerObservation(server({ last_error: 'timeout:legacy failure' }), undefined, now).health).toBe('stale');
expect(
selectServerObservation(
server({ last_error: 'timeout:legacy failure' }),
undefined,
now
).health
).toBe('stale');
});
it('accepts telemetry and container evidence through 90 seconds, then expires each independently', () => {
expect(selectServerObservation(server(), report(), now + 90_000)).toMatchObject({ health: 'online', status_source: 'telemetry' });
expect(selectServerObservation(server(), report(90_001), now + 90_001).health).toBe('stale');
expect(selectServerObservation(server(), report(), now + 90_000)).toMatchObject({
health: 'online',
status_source: 'telemetry'
});
expect(selectServerObservation(server(), report(90_001), now + 90_001).health).toBe(
'stale'
);
const current = server({ container_checked_at: at(90_001) });
expect(selectServerObservation(current, report(), now + 90_001).health).toBe('stale');
expect(freshTelemetry(report(-90_000), now)).toBe(true);
@@ -41,7 +88,12 @@ describe('shared server observation', () => {
it('selects the telemetry count and roster together and keeps raw RCON evidence', () => {
const raw = server({ last_error: 'timeout' });
const selected = selectServerObservation(server({ rcon_status: raw }), report(), now);
expect(selected).toMatchObject({ health: 'online', status_source: 'telemetry', last_players: 3, last_error: null });
expect(selected).toMatchObject({
health: 'online',
status_source: 'telemetry',
last_players: 3,
last_error: null
});
expect(selected.roster).toHaveLength(3);
expect(selected.roster?.[0]).toMatchObject({ ping: null, score: 5000 });
expect(selected.rcon_status).toBe(raw);
@@ -49,157 +101,333 @@ describe('shared server observation', () => {
it('supplements telemetry members with GUID-matched pings without changing scores or names', () => {
const telemetry = report();
const raw = server({ roster: [
{ ...telemetry.players[1], num: 2, score: 10, ping: 0, name: 'Old name' },
{ ...telemetry.players[0], num: 3, score: 20, ping: 999 },
{ ...telemetry.players[2], num: 4, score: 30, ping: 45, guid: 'different-player', name: 'Different player' }
] });
const raw = server({
roster: [
{ ...telemetry.players[1], num: 2, score: 10, ping: 0, name: 'Old name' },
{ ...telemetry.players[0], num: 3, score: 20, ping: 999 },
{
...telemetry.players[2],
num: 4,
score: 30,
ping: 45,
guid: 'different-player',
name: 'Different player'
}
]
});
const selected = selectServerObservation(server({ rcon_status: raw }), telemetry, now);
expect(selected.roster).toEqual(telemetry.players.map((player, i) => ({
name: player.name, guid: player.guid, num: null, score: 5000, ping: [999, 0, null][i]
})));
expect(selected.roster).toEqual(
telemetry.players.map((player, i) => ({
name: player.name,
guid: player.guid,
num: null,
score: 5000,
ping: [999, 0, null][i]
}))
);
expect(selected.last_players).toBe(3);
expect(selected.rcon_status).toBe(raw);
// A new telemetry report must not renew the RCON sample's age.
const expired = selectServerObservation({ ...selected, container_checked_at: at(90_001) }, report(90_001), now + 90_001);
const expired = selectServerObservation(
{ ...selected, container_checked_at: at(90_001) },
report(90_001),
now + 90_001
);
expect(expired.health).toBe('online');
expect(expired.roster?.map(player => player.ping)).toEqual([null, null, null]);
expect(expired.roster?.map((player) => player.ping)).toEqual([null, null, null]);
});
it('prefers telemetry ping, including zero, and fills absent or null readings from RCON', () => {
const telemetry = report();
telemetry.players[0].ping = 0;
telemetry.players[1].ping = null;
const raw = server({ roster: telemetry.players.map(player => ({ ...player, num: 0, score: 10, ping: 42 })) });
expect(selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([0, 42, 42]);
const raw = server({
roster: telemetry.players.map((player) => ({ ...player, num: 0, score: 10, ping: 42 }))
});
expect(
selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.map(
(player) => player.ping
)
).toEqual([0, 42, 42]);
});
it.each([
{ last_seen_at: at(-90_001) }, { last_seen_at: at(2000) }, { last_seen_at: null },
{ last_error: 'timeout', rcon_failures: 2 }, { last_map: 'other-map' }
])('does not supplement pings from unusable RCON evidence: %j', changes => {
{ last_seen_at: at(-90_001) },
{ last_seen_at: at(2000) },
{ last_seen_at: null },
{ last_error: 'timeout', rcon_failures: 2 },
{ last_map: 'other-map' }
])('does not supplement pings from unusable RCON evidence: %j', (changes) => {
const telemetry = report();
const raw = server({ ...changes, roster: [{ ...telemetry.players[0], num: 0, score: 100, ping: 42 }] });
expect(selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping).toBeNull();
const raw = server({
...changes,
roster: [{ ...telemetry.players[0], num: 0, score: 100, ping: 42 }]
});
expect(
selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping
).toBeNull();
});
it('requires RCON samples after the latest lifecycle and tolerates one transient failure', () => {
const telemetry = report();
const raw = server({ last_seen_at: at(-90_000), roster: [{ ...telemetry.players[0], num: 0, score: 100, ping: 42 }] });
expect(selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping).toBe(42);
expect(selectServerObservation(server({ rcon_status: raw, telemetry_after: at(-90_000) }), telemetry, now).roster?.[0].ping).toBeNull();
expect(selectServerObservation(server({ rcon_status: { ...raw, last_error: 'timeout', rcon_failures: 1 } }), telemetry, now).roster?.[0].ping).toBe(42);
const raw = server({
last_seen_at: at(-90_000),
roster: [{ ...telemetry.players[0], num: 0, score: 100, ping: 42 }]
});
expect(
selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping
).toBe(42);
expect(
selectServerObservation(
server({ rcon_status: raw, telemetry_after: at(-90_000) }),
telemetry,
now
).roster?.[0].ping
).toBeNull();
expect(
selectServerObservation(
server({ rcon_status: { ...raw, last_error: 'timeout', rcon_failures: 1 } }),
telemetry,
now
).roster?.[0].ping
).toBe(42);
});
it.each(['duplicate', 'null', 'negative', 'invalid'])('rejects %s ping matches', mode => {
it.each(['duplicate', 'null', 'negative', 'invalid'])('rejects %s ping matches', (mode) => {
const telemetry = report();
const player = { ...telemetry.players[0], num: 0, score: 100, ping: 42 as number | null, guid: telemetry.players[0].guid as string | null };
const player = {
...telemetry.players[0],
num: 0,
score: 100,
ping: 42 as number | null,
guid: telemetry.players[0].guid as string | null
};
if (mode === 'null') player.ping = null;
if (mode === 'negative') player.ping = -1;
if (mode === 'invalid') player.ping = NaN;
const raw = server({ roster: mode === 'duplicate' ? [player, { ...player, ping: 50 }] : [player] });
expect(selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping).toBeNull();
const raw = server({
roster: mode === 'duplicate' ? [player, { ...player, ping: 50 }] : [player]
});
expect(
selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.[0].ping
).toBeNull();
});
it.each(['t5', 't6'])('does not match %s players by name when GUIDs differ or are missing', game => {
const telemetry = report(0, 2);
telemetry.players[0].name = 'EyelessGreem';
telemetry.players[1].name = '_frxn.3';
const raw = server({ roster: [
{ num: 0, score: 940, ping: 91, guid: '6C4C20', name: 'EyelessGreem' },
{ num: 1, score: 150, ping: 151, guid: null, name: '_frxn.3' }
] });
const selected = selectServerObservation(server({ game, rcon_status: raw }), telemetry, now);
expect(selected.roster?.map(player => player.ping)).toEqual([null, null]);
});
it.each(['t5', 't6'])(
'does not match %s players by name when GUIDs differ or are missing',
(game) => {
const telemetry = report(0, 2);
telemetry.players[0].name = 'EyelessGreem';
telemetry.players[1].name = '_frxn.3';
const raw = server({
roster: [
{ num: 0, score: 940, ping: 91, guid: '6C4C20', name: 'EyelessGreem' },
{ num: 1, score: 150, ping: 151, guid: null, name: '_frxn.3' }
]
});
const selected = selectServerObservation(
server({ game, rcon_status: raw }),
telemetry,
now
);
expect(selected.roster?.map((player) => player.ping)).toEqual([null, null]);
}
);
it.each([
['6C4C20', '7097376'], ['6c45dd', '7095773'], ['00006C4C20', '0007097376'],
['0x6C45DD', '7095773'], ['10', '16'], ['20000000000001', '9007199254740993']
['6C4C20', '7097376'],
['6c45dd', '7095773'],
['00006C4C20', '0007097376'],
['0x6C45DD', '7095773'],
['10', '16'],
['20000000000001', '9007199254740993']
])('matches T6 hex GUID %s to decimal %s despite different names', (hex, decimal) => {
const telemetry = report(0, 1);
telemetry.players[0].guid = decimal;
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: hex, name: 'Previous name' }] });
const selected = selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now);
expect(selected.roster?.[0]).toMatchObject({ guid: decimal, name: 'Player 1', ping: 91, score: 5000 });
const raw = server({
roster: [{ num: 0, score: 100, ping: 91, guid: hex, name: 'Previous name' }]
});
const selected = selectServerObservation(
server({ game: 't6', rcon_status: raw }),
telemetry,
now
);
expect(selected.roster?.[0]).toMatchObject({
guid: decimal,
name: 'Player 1',
ping: 91,
score: 5000
});
expect(selected.rcon_status?.roster?.[0].guid).toBe(hex);
});
it.each(['t5', undefined])('keeps decimal-looking GUIDs literal for game %s', game => {
it.each(['t5', undefined])('keeps decimal-looking GUIDs literal for game %s', (game) => {
const telemetry = report(0, 2);
telemetry.players[0].guid = '10';
telemetry.players[1].guid = '16';
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Previous name' }] });
expect(selectServerObservation(server({ game, rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([91, null]);
const raw = server({
roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Previous name' }]
});
expect(
selectServerObservation(server({ game, rcon_status: raw }), telemetry, now).roster?.map(
(player) => player.ping
)
).toEqual([91, null]);
});
it('treats digit-only T6 GUIDs as hexadecimal regardless of player names', () => {
const telemetry = report(0, 2);
telemetry.players[0].guid = '10';
telemetry.players[1].guid = '16';
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Player 1' }] });
expect(selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([null, 91]);
const raw = server({
roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Player 1' }]
});
expect(
selectServerObservation(
server({ game: 't6', rcon_status: raw }),
telemetry,
now
).roster?.map((player) => player.ping)
).toEqual([null, 91]);
});
it('rejects duplicate RCON GUIDs after normalization', () => {
const telemetry = report(0, 1);
telemetry.players[0].guid = '7097376';
const raw = server({ roster: [
{ num: 0, score: 100, ping: 91, guid: '6C4C20', name: 'Player 1' },
{ num: 1, score: 100, ping: 42, guid: '006c4c20', name: 'Other player' }
] });
expect(selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now).roster?.[0].ping).toBeNull();
const raw = server({
roster: [
{ num: 0, score: 100, ping: 91, guid: '6C4C20', name: 'Player 1' },
{ num: 1, score: 100, ping: 42, guid: '006c4c20', name: 'Other player' }
]
});
expect(
selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now)
.roster?.[0].ping
).toBeNull();
});
it('matches GUIDs regardless of conflicting names', () => {
const telemetry = report(0, 2);
const raw = server({ roster: [
{ num: 0, score: 100, ping: 42, guid: telemetry.players[1].guid, name: telemetry.players[0].name },
{ num: 1, score: 100, ping: 99, guid: 'another-guid', name: telemetry.players[1].name }
] });
expect(selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([null, 42]);
const raw = server({
roster: [
{
num: 0,
score: 100,
ping: 42,
guid: telemetry.players[1].guid,
name: telemetry.players[0].name
},
{
num: 1,
score: 100,
ping: 99,
guid: 'another-guid',
name: telemetry.players[1].name
}
]
});
expect(
selectServerObservation(server({ rcon_status: raw }), telemetry, now).roster?.map(
(player) => player.ping
)
).toEqual([null, 42]);
});
it('uses the latest HTTP or streamed telemetry and falls back without extending freshness', () => {
const original = server();
const selected = selectServerObservation(server({ rcon_status: original, telemetry: report(0, 2) }), report(-1000), now);
const selected = selectServerObservation(
server({ rcon_status: original, telemetry: report(0, 2) }),
report(-1000),
now
);
expect(selected.last_players).toBe(2);
const fallback = selectServerObservation({ ...selected, container_checked_at: at(91_000),
rcon_status: { ...original, last_seen_at: at(40_000) } }, undefined, now + 91_000);
expect(fallback).toMatchObject({ status_source: 'rcon', health: 'online', last_players: 4, last_seen_at: at(40_000) });
const fallback = selectServerObservation(
{
...selected,
container_checked_at: at(91_000),
rcon_status: { ...original, last_seen_at: at(40_000) }
},
undefined,
now + 91_000
);
expect(fallback).toMatchObject({
status_source: 'rcon',
health: 'online',
last_players: 4,
last_seen_at: at(40_000)
});
});
it('displays telemetry pings and clears missing readings on subsequent reports', () => {
const telemetry = report(0, 4);
telemetry.players = telemetry.players.map((player, i) => ({ ...player, ping: [0, 42, 999, null][i] }));
telemetry.players = telemetry.players.map((player, i) => ({
...player,
ping: [0, 42, 999, null][i]
}));
const selected = selectServerObservation(server(), telemetry, now);
expect(selected.roster?.map(player => player.ping)).toEqual([0, 42, 999, null]);
expect(selectServerObservation(selected, report(1, 4), now + 1).roster?.map(player => player.ping)).toEqual([null, null, null, null]);
const start = { ...gameEvent('game_start', 0, 1, 'managed'), event: 'game_start' as const, receivedAt: at(),
players: [{ name: 'Player 1', guid: '123', ping: 25 }] };
expect(selectServerObservation(server(), start, now).roster?.[0]).toMatchObject({ score: null, ping: 25 });
expect(selected.roster?.map((player) => player.ping)).toEqual([0, 42, 999, null]);
expect(
selectServerObservation(selected, report(1, 4), now + 1).roster?.map(
(player) => player.ping
)
).toEqual([null, null, null, null]);
const start = {
...gameEvent('game_start', 0, 1, 'managed'),
event: 'game_start' as const,
receivedAt: at(),
players: [{ name: 'Player 1', guid: '123', ping: 25 }]
};
expect(selectServerObservation(server(), start, now).roster?.[0]).toMatchObject({
score: null,
ping: 25
});
});
it.each(['starting', 'restarting', 'unhealthy', 'exited'])('telemetry cannot override container state %s', state => {
const selected = selectServerObservation(server({ health: state === 'starting' || state === 'restarting' ? 'starting' : 'offline', container_state: state }), report(), now);
expect(selected.health).not.toBe('online');
expect(selected.last_players).toBeNull();
expect(selected.roster).toBeNull();
});
it.each(['starting', 'restarting', 'unhealthy', 'exited'])(
'telemetry cannot override container state %s',
(state) => {
const selected = selectServerObservation(
server({
health: state === 'starting' || state === 'restarting' ? 'starting' : 'offline',
container_state: state
}),
report(),
now
);
expect(selected.health).not.toBe('online');
expect(selected.last_players).toBeNull();
expect(selected.roster).toBeNull();
}
);
it('uses fresh RCON with unknown container state, but does not substitute telemetry or extend freshness', () => {
const raw = server({ container_state: 'unknown', container_checked_at: at(-60_000) });
expect(selectServerObservation(raw, report(), now)).toMatchObject({ health: 'online', last_players: 4, status_source: 'rcon' });
expect(selectServerObservation(raw, report(), now)).toMatchObject({
health: 'online',
last_players: 4,
status_source: 'rcon'
});
expect(selectServerObservation(raw, report(91_000), now + 91_000).health).toBe('stale');
expect(selectServerObservation(raw, report(121_000), now + 121_000).health).toBe('offline');
expect(selectServerObservation({ ...raw, telemetry_after: at() }, report(), now).health).toBe('offline');
expect(selectServerObservation({ ...raw, last_seen_at: null }, report(), now).health).toBe('offline');
expect(
selectServerObservation({ ...raw, telemetry_after: at() }, report(), now).health
).toBe('offline');
expect(selectServerObservation({ ...raw, last_seen_at: null }, report(), now).health).toBe(
'offline'
);
});
it('requires fresh container evidence and reports after the latest lifecycle submission', () => {
expect(selectServerObservation(server({ container_checked_at: at(-91_000) }), report(), now).health).not.toBe('online');
expect(selectServerObservation(server({ health: 'provisioning' }), report(), now).health).toBe('provisioning');
expect(selectServerObservation(server({ telemetry_after: at() }), report(-1), now).health).toBe('offline');
expect(
selectServerObservation(server({ container_checked_at: at(-91_000) }), report(), now)
.health
).not.toBe('online');
expect(
selectServerObservation(server({ health: 'provisioning' }), report(), now).health
).toBe('provisioning');
expect(
selectServerObservation(server({ telemetry_after: at() }), report(-1), now).health
).toBe('offline');
expect(freshTelemetry(report(), now, 'invalid')).toBe(false);
expect(freshTelemetry(report(2000), now)).toBe(false);
expect(freshTelemetry(report(-91_000), now)).toBe(false);
@@ -211,7 +439,13 @@ describe('shared server observation', () => {
const active = selectServerObservation(server(), start, now);
expect(active.roster?.[0]).toMatchObject({ score: null, ping: null });
expect(active.game_started_at).toBe(at());
expect(selectServerObservation({ ...active, game_ended_at: at() }, report(1, 0), now + 1))
.toMatchObject({ last_players: 0, roster: [], game_started_at: null, game_ended_at: null });
expect(
selectServerObservation({ ...active, game_ended_at: at() }, report(1, 0), now + 1)
).toMatchObject({
last_players: 0,
roster: [],
game_started_at: null,
game_ended_at: null
});
});
});