adds round telemetry support

This commit is contained in:
2026-09-17 14:35:42 -04:00
parent 8e04a190b3
commit 858704bb10
12 changed files with 191 additions and 3 deletions
+65
View File
@@ -0,0 +1,65 @@
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import { newFixture, type Fixture } from '../helpers/fixture.js';
let fixture: Fixture;
beforeEach(async () => {
vi.resetModules();
fixture = await newFixture();
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
await seedServerRegistry();
});
afterEach(async () => fixture.cleanup());
const payload = { event: 'round_change', server: 't5_zombie_theater_1', round: 17, players: 4 };
async function post(body: unknown, contentType = 'application/json') {
const { POST } = await import('../../src/routes/api/telemetry/+server.js');
return POST({ request: new Request('http://localhost/api/telemetry', {
method: 'POST', headers: { 'Content-Type': contentType }, body: JSON.stringify(body)
}) });
}
it('persists reports, broadcasts updates, and leaves RCON safety data untouched', async () => {
const { subscribe } = await import('../../src/lib/server/pipeline/bus.js');
const listener = vi.fn();
const unsubscribe = subscribe(listener);
expect((await post(payload)).status).toBe(200);
expect(listener).toHaveBeenCalledWith(expect.objectContaining({ type: 'server-telemetry' }));
unsubscribe();
// A new match can legitimately report a lower round; retries overwrite one row.
await post({ ...payload, round: 1 });
await post({ ...payload, round: 1 });
const { closeDb } = await import('../../src/lib/server/db/index.js');
closeDb();
const { listTelemetry } = await import('../../src/lib/server/fleet/telemetry.js');
expect(listTelemetry()).toEqual([expect.objectContaining({ round: 1, players: 4 })]);
const { getServer } = await import('../../src/lib/server/fleet/servers.js');
expect(getServer(payload.server)?.last_players).toBeNull();
const { GET } = await import('../../src/routes/api/events/servers/+server.js');
const response = await GET();
const reader = response.body!.getReader();
await reader.read(); // greeting
const snapshot = await reader.read();
expect(new TextDecoder().decode(snapshot.value)).toContain('"round":1');
await reader.cancel();
});
it.each([null, [], {}, { ...payload, event: 'other' }, { ...payload, round: -1 },
{ ...payload, round: 1.5 }, { ...payload, round: '17' }, { ...payload, players: -1 },
{ ...payload, players: null }])('rejects invalid telemetry: %j', async (body) => {
expect((await post(body)).status).toBe(400);
});
it('rejects unknown and destroyed servers and non-JSON requests', async () => {
expect((await post({ ...payload, server: 'unknown' })).status).toBe(404);
expect((await post(payload, 'text/plain')).status).toBe(415);
const { setServerField } = await import('../../src/lib/server/fleet/servers.js');
setServerField(payload.server, 'destroyed_at', new Date().toISOString());
expect((await post(payload)).status).toBe(404);
});
it('rejects malformed JSON', async () => {
const { POST } = await import('../../src/routes/api/telemetry/+server.js');
expect((await POST({ request: new Request('http://localhost/api/telemetry', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{'
}) })).status).toBe(400);
});