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
+123 -33
View File
@@ -18,7 +18,8 @@ afterEach(async () => {
});
async function diagnostics(name = server) {
const { GET } = await import('../../src/routes/api/servers/[name]/telemetry-requests/+server.js');
const { GET } =
await import('../../src/routes/api/servers/[name]/telemetry-requests/+server.js');
return GET({ params: { name } });
}
@@ -27,7 +28,9 @@ it('returns uncached, empty diagnostics for a registered server before any reque
expect(response.status).toBe(200);
expect(response.headers.get('cache-control')).toContain('no-store');
expect(await response.json()).toEqual({
ok: true, requests: [], retention: { maxPerServer: 200, maxAgeDays: 7, maxTotal: 10000 }
ok: true,
requests: [],
retention: { maxPerServer: 200, maxAgeDays: 7, maxTotal: 10000 }
});
expect((await diagnostics('unknown')).status).toBe(404);
});
@@ -44,9 +47,18 @@ it('persists the received payload and response, isolates each server, and lists
const body = await (await diagnostics()).json();
expect(body.requests).toHaveLength(2);
expect(body.requests[0]).toEqual({
id: expect.any(Number), server, receivedAt: expect.any(String), status: 200, error: null,
event: 'round_change', map: 'zombie_theater', round: 3, playerCount: 4,
contentType: 'application/json', payload: JSON.stringify(latest), payloadTruncated: false
id: expect.any(Number),
server,
receivedAt: expect.any(String),
status: 200,
error: null,
event: 'round_change',
map: 'zombie_theater',
round: 3,
playerCount: 4,
contentType: 'application/json',
payload: JSON.stringify(latest),
payloadTruncated: false
});
expect(body.requests[0].id).toBeGreaterThan(body.requests[1].id);
expect(body.requests[1].payload).toBe(JSON.stringify(first));
@@ -62,10 +74,18 @@ it('records rejected player data with the same validation reason returned to the
const result = await response.json();
expect(result.error).toContain('players[0].kills');
const { requests } = await (await diagnostics()).json();
expect(requests).toEqual([expect.objectContaining({
server, status: 400, error: result.error, event: 'round_change', round: 5,
playerCount: 1, payload: JSON.stringify(invalid), payloadTruncated: false
})]);
expect(requests).toEqual([
expect.objectContaining({
server,
status: 400,
error: result.error,
event: 'round_change',
round: 5,
playerCount: 1,
payload: JSON.stringify(invalid),
payloadTruncated: false
})
]);
const { listTelemetry } = await import('../../src/lib/server/fleet/telemetry.js');
expect(listTelemetry()).toEqual([]);
});
@@ -76,14 +96,24 @@ it('records identifiable JSON sent with an unsupported or missing Content-Type',
const { POST } = await import('../../src/routes/api/telemetry/+server.js');
// A byte body avoids Request automatically assigning text/plain to a string body.
const request = new Request('http://localhost/api/telemetry', {
method: 'POST', headers: telemetryAuthHeaders(), body: new TextEncoder().encode(JSON.stringify(payload))
method: 'POST',
headers: telemetryAuthHeaders(),
body: new TextEncoder().encode(JSON.stringify(payload))
});
expect(request.headers.get('content-type')).toBeNull();
expect((await POST({ request })).status).toBe(415);
const { requests } = await (await diagnostics()).json();
expect(requests).toEqual([
expect.objectContaining({ status: 415, contentType: null, error: 'Content-Type must be application/json' }),
expect.objectContaining({ status: 415, contentType: 'text/plain', payload: JSON.stringify(payload) })
expect.objectContaining({
status: 415,
contentType: null,
error: 'Content-Type must be application/json'
}),
expect.objectContaining({
status: 415,
contentType: 'text/plain',
payload: JSON.stringify(payload)
})
]);
});
@@ -93,7 +123,13 @@ it('distinguishes a zero round and an empty scoreboard from absent or invalid fi
await post({ server, players: 4 });
const { requests } = await (await diagnostics()).json();
expect(requests).toEqual([
expect.objectContaining({ status: 400, event: null, map: null, round: null, playerCount: null }),
expect.objectContaining({
status: 400,
event: null,
map: null,
round: null,
playerCount: null
}),
expect.objectContaining({ status: 200, event: 'game_start', round: null, playerCount: 0 }),
expect.objectContaining({ status: 200, event: 'round_change', round: 0, playerCount: 0 })
]);
@@ -109,14 +145,23 @@ it('keeps unknown and destroyed server rejections and matches the submitted serv
setServerField(server, 'destroyed_at', new Date().toISOString());
expect((await post(gameEvent('game_end', 1))).status).toBe(404);
const { listTelemetryRequests } = await import('../../src/lib/server/fleet/telemetry-requests.js');
expect(listTelemetryRequests(unknown)).toEqual([expect.objectContaining({ server: unknown, status: 404 })]);
expect(listTelemetryRequests(spaced)).toEqual([expect.objectContaining({ server: spaced, status: 404 })]);
const { listTelemetryRequests } =
await import('../../src/lib/server/fleet/telemetry-requests.js');
expect(listTelemetryRequests(unknown)).toEqual([
expect.objectContaining({ server: unknown, status: 404 })
]);
expect(listTelemetryRequests(spaced)).toEqual([
expect.objectContaining({ server: spaced, status: 404 })
]);
expect((await diagnostics(unknown)).status).toBe(404);
const response = await diagnostics();
expect(response.status).toBe(200);
expect((await response.json()).requests).toEqual([
expect.objectContaining({ server, status: 404, error: 'Server is not registered or has been destroyed' }),
expect.objectContaining({
server,
status: 404,
error: 'Server is not registered or has been destroyed'
}),
expect.objectContaining({ server, status: 200 })
]);
});
@@ -124,21 +169,30 @@ it('keeps unknown and destroyed server rejections and matches the submitted serv
it('does not attribute malformed JSON or unusable server names to a server', async () => {
const { POST } = await import('../../src/routes/api/telemetry/+server.js');
const malformed = new Request('http://localhost/api/telemetry', {
method: 'POST', headers: { 'Content-Type': 'application/json', ...telemetryAuthHeaders() }, body: `{"server":"${server}",`
method: 'POST',
headers: { 'Content-Type': 'application/json', ...telemetryAuthHeaders() },
body: `{"server":"${server}",`
});
expect((await POST({ request: malformed })).status).toBe(400);
for (const value of [undefined, null, '', ' ', 123, 's'.repeat(256)]) {
expect((await post({ ...gameEvent('game_start'), server: value })).status).toBe(400);
}
const { db } = await import('../../src/lib/server/db/index.js');
expect(db().prepare('SELECT COUNT(*) AS count FROM telemetry_requests').get()).toEqual({ count: 0 });
expect(db().prepare('SELECT COUNT(*) AS count FROM telemetry_requests').get()).toEqual({
count: 0
});
});
it('bounds stored payloads and marks truncation without rejecting a valid event', async () => {
const payload = { ...gameEvent('round_change', 4), diagnostic: 'x'.repeat(20_000) };
expect((await post(payload)).status).toBe(200);
const { requests } = await (await diagnostics()).json();
expect(requests[0]).toMatchObject({ status: 200, round: 4, playerCount: 4, payloadTruncated: true });
expect(requests[0]).toMatchObject({
status: 200,
round: 4,
playerCount: 4,
payloadTruncated: true
});
expect(requests[0].payload).toBe(JSON.stringify(payload).slice(0, 16 * 1024));
});
@@ -154,7 +208,9 @@ it('records processing failures after rollback without changing the scoreboard,
const matches = db().prepare('SELECT * FROM leaderboard_matches ORDER BY id').all();
const listener = vi.fn();
const unsubscribe = subscribe(listener);
vi.spyOn(beta, 'recordGameEvent').mockImplementationOnce(() => { throw new Error('Injected beta processing failure'); });
vi.spyOn(beta, 'recordGameEvent').mockImplementationOnce(() => {
throw new Error('Injected beta processing failure');
});
const response = await post(gameEvent('game_end', 99));
unsubscribe();
@@ -165,7 +221,12 @@ it('records processing failures after rollback without changing the scoreboard,
expect(db().prepare('SELECT * FROM leaderboard_matches ORDER BY id').all()).toEqual(matches);
expect(listener).not.toHaveBeenCalled();
expect((await (await diagnostics()).json()).requests).toEqual([
expect.objectContaining({ status: 500, event: 'game_end', round: 99, error: 'Could not process telemetry: Injected beta processing failure' }),
expect.objectContaining({
status: 500,
event: 'game_end',
round: 99,
error: 'Could not process telemetry: Injected beta processing failure'
}),
expect.objectContaining({ status: 200, round: 3 })
]);
});
@@ -183,15 +244,19 @@ it('still accepts and broadcasts telemetry if storing diagnostics fails', async
expect(response.status).toBe(200);
const { listTelemetry } = await import('../../src/lib/server/fleet/telemetry.js');
expect(listTelemetry()).toEqual([expect.objectContaining({ ...payload, receivedAt: expect.any(String) })]);
expect(listTelemetry()).toEqual([
expect.objectContaining({ ...payload, receivedAt: expect.any(String) })
]);
expect(listener).toHaveBeenCalledWith({
type: 'server-telemetry', entries: [expect.objectContaining({ ...payload, receivedAt: expect.any(String) })]
type: 'server-telemetry',
entries: [expect.objectContaining({ ...payload, receivedAt: expect.any(String) })]
});
});
it('retains only the latest 200 requests for a server and removes requests older than seven days', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const { recordTelemetryRequest, listTelemetryRequests } = await import('../../src/lib/server/fleet/telemetry-requests.js');
const { recordTelemetryRequest, listTelemetryRequests } =
await import('../../src/lib/server/fleet/telemetry-requests.js');
const now = new Date().toISOString();
const old = new Date(Date.now() - 8 * 86_400_000).toISOString();
const insert = db().prepare(`INSERT INTO telemetry_requests
@@ -201,27 +266,52 @@ it('retains only the latest 200 requests for a server and removes requests older
insert.run(otherServer, now);
for (let i = 0; i < 205; i++) insert.run(server, now);
})();
recordTelemetryRequest({ value: gameEvent('round_change', 9), receivedAt: now, status: 200, error: null, contentType: 'application/json' });
recordTelemetryRequest({
value: gameEvent('round_change', 9),
receivedAt: now,
status: 200,
error: null,
contentType: 'application/json'
});
const requests = listTelemetryRequests(server);
expect(requests).toHaveLength(200);
expect(requests[0]).toMatchObject({ round: 9 });
expect(requests.map(entry => entry.id)).toEqual([...requests.map(entry => entry.id)].sort((a, b) => b - a));
expect(requests.map((entry) => entry.id)).toEqual(
[...requests.map((entry) => entry.id)].sort((a, b) => b - a)
);
expect(listTelemetryRequests(otherServer)).toHaveLength(1);
expect(db().prepare('SELECT COUNT(*) AS count FROM telemetry_requests WHERE received_at = ?').get(old)).toEqual({ count: 0 });
expect(
db()
.prepare('SELECT COUNT(*) AS count FROM telemetry_requests WHERE received_at = ?')
.get(old)
).toEqual({ count: 0 });
});
it('caps global diagnostic history even when rejected requests name many unknown servers', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const { recordTelemetryRequest } = await import('../../src/lib/server/fleet/telemetry-requests.js');
const { recordTelemetryRequest } =
await import('../../src/lib/server/fleet/telemetry-requests.js');
const now = new Date().toISOString();
const insert = db().prepare(`INSERT INTO telemetry_requests
(server, received_at, status, payload, payload_truncated) VALUES (?, ?, 404, '{}', 0)`);
db().transaction(() => {
for (let i = 0; i < 10_005; i++) insert.run(`unknown-${i}`, now);
})();
recordTelemetryRequest({ value: gameEvent('game_start'), receivedAt: now, status: 200, error: null, contentType: 'application/json' });
expect(db().prepare('SELECT COUNT(*) AS count FROM telemetry_requests').get()).toEqual({ count: 10_000 });
expect(db().prepare('SELECT server FROM telemetry_requests ORDER BY id DESC LIMIT 1').get()).toEqual({ server });
expect(db().prepare("SELECT id FROM telemetry_requests WHERE server = 'unknown-0'").get()).toBeUndefined();
recordTelemetryRequest({
value: gameEvent('game_start'),
receivedAt: now,
status: 200,
error: null,
contentType: 'application/json'
});
expect(db().prepare('SELECT COUNT(*) AS count FROM telemetry_requests').get()).toEqual({
count: 10_000
});
expect(
db().prepare('SELECT server FROM telemetry_requests ORDER BY id DESC LIMIT 1').get()
).toEqual({ server });
expect(
db().prepare("SELECT id FROM telemetry_requests WHERE server = 'unknown-0'").get()
).toBeUndefined();
});