Preserve conversation excerpts and deduplicate case evidence

This commit is contained in:
2026-10-17 19:35:52 -04:00
parent 83f1c91877
commit ef96a2c308
13 changed files with 1222 additions and 50 deletions
+78
View File
@@ -453,3 +453,81 @@ it('keeps decision history writable when a case has reached its evidence limit',
expect((await detail(id)).case).toMatchObject({ status: 'open', revision: 3 });
expect((await detail(id)).case.evidence).toHaveLength(102);
});
it('preserves all participants and selected authors, distinguishes repeated messages, and deduplicates attachments', async () => {
const { saveTelemetry } = await import('../../src/lib/server/fleet/telemetry.js');
const { matchChatTimeline } = await import('../../src/lib/server/fleet/chat.js');
saveTelemetry({
server,
map: 'zombie_theater',
event: 'game_start',
players: [{ guid: '123', name: 'Player' }],
receivedAt: clock
});
const { id: matchId } = handle.prepare('SELECT id FROM leaderboard_matches').get() as {
id: number;
};
const insert = handle.prepare('INSERT INTO match_chat VALUES(?,?,?,?)');
for (let i = 0; i < 22; i++) {
const subject = i > 0 && i < 20;
insert.run(
matchId,
subject ? 'repeat' : `context-${i}`,
subject ? i : 1,
JSON.stringify({
type: 'message',
context: { realTime: null, round: null, matchElapsedMs: null },
message: {
time: '1:01',
guid: subject ? '123' : '456',
player: subject ? 'Subject' : 'Witness',
slot: subject ? 1 : 2,
message: subject ? 'Repeated message' : `Context ${i}`
}
})
);
}
const entries = matchChatTimeline(matchId);
expect(new Set(entries.map((entry) => entry.messageId)).size).toBe(22);
const { id } = await create();
await mutate({
op: 'attach-evidence',
id,
revision: 1,
evidence: { matchId, messageIds: entries.slice(1, 20).map((entry) => entry.messageId) }
});
let data = await detail(id);
expect(data.case.evidence).toHaveLength(1);
const snapshot = JSON.parse(data.case.evidence[0].snapshot!);
expect(snapshot.entries).toHaveLength(22);
expect(snapshot.selectedMessageIds).toHaveLength(19);
expect(snapshot.highlightedMessageIds).toHaveLength(19);
expect(snapshot.entries[0].message.player).toBe('Witness');
await mutate({
op: 'attach-evidence',
id,
revision: 2,
evidence: { matchId, message: 'Repeated message' }
});
data = await detail(id);
expect(data.case.revision).toBe(2);
expect(data.case.evidence).toHaveLength(1);
await expect(
mutate({
op: 'attach-evidence',
id,
revision: 2,
evidence: { matchId, messageIds: ['forged'] }
})
).rejects.toThrow('identifiers');
const legacy = await create();
await mutate({
op: 'attach-evidence',
id: legacy.id,
revision: 1,
evidence: { matchId, message: 'Repeated message' }
});
expect((await detail(legacy.id)).case.evidence).toHaveLength(19);
handle.prepare('DELETE FROM leaderboard_matches').run();
expect(JSON.parse((await detail(id)).case.evidence[0].snapshot!).entries).toHaveLength(22);
});