Preserve deleted server history

Retain historical server identities, results, and evidence after deletion. Mark archived entries as Deleted and prevent old work from targeting servers that reuse the same name.

Fixes #16.
This commit is contained in:
2026-09-27 21:48:55 -04:00
parent 53b29f726c
commit 981fb27c1c
61 changed files with 929 additions and 223 deletions
+24
View File
@@ -294,6 +294,30 @@ it('backfills valid saved telemetry once while skipping malformed and legacy sna
expect(await records()).toEqual([expect.objectContaining({ kills: 150, headshots: 80, rounds_survived: 1 })]);
});
it('keeps deleted match identities and starts a separate match when the server name is reused', async () => {
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
const { serverMatchHistory } = await import('../../src/lib/server/fleet/match-history.js');
const { getPlayerProfile } = await import('../../src/lib/server/fleet/player-profile.js');
await report('heartbeat', 8, [player(undefined, { kills: 120 })]);
const original = serverMatchHistory(server).matches[0];
db().prepare('DELETE FROM servers WHERE name = ?').run(server);
await seedServerRegistry();
// Even the same report bytes must belong to the replacement's own match.
await report('heartbeat', 8, [player(undefined, { kills: 120 })]);
closeDb();
const history = serverMatchHistory(server).matches;
expect(history).toHaveLength(2);
expect(history.find(match => match.id === original.id)).toMatchObject({
server, server_deleted_at: expect.any(String), status: 'interrupted'
});
expect(history.find(match => match.id !== original.id)).toMatchObject({ server, server_deleted_at: null, status: 'active' });
const profile = getPlayerProfile('t5', player().guid)!;
expect(profile.stats.gamesPlayed).toBe(2);
expect(profile.matches.find(match => match.id === original.id)?.server_deleted_at).toEqual(expect.any(String));
expect(profile.stats.kills).toBe(240);
});
it('does not fabricate players for an empty match', async () => {
await report('game_start', 0, []);
await report('game_end', 0, []);