import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import { newFixture, type Fixture } from '../helpers/fixture.js'; import { moderationGuid } from '../../src/lib/moderation.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture(); }); afterEach(async () => fixture.cleanup()); it('keeps large identities exact and normalizes only known representations', () => { expect(moderationGuid('t6', '110000100000001', 'rcon')).toBe('76561197960265729'); expect(moderationGuid('t6', '076561197960265729')).toBe('76561197960265729'); expect(moderationGuid('t5', 'DEADbeef', 'rcon')).toBe('DEADbeef'); expect(moderationGuid('t5', 'Some:opaque_ID')).toBe('Some:opaque_ID'); }); it('preserves aliases and moderation identity independently of gameplay retention', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const { observeModerationPlayers } = await import('../../src/lib/server/moderation/core.js'); const handle = db(); observeModerationPlayers( handle, 't6', [{ guid: '000123', name: 'Old name' }], '2026-10-01T00:00:00.000Z' ); observeModerationPlayers( handle, 't6', [{ guid: '123', name: 'New name' }], '2026-10-02T00:00:00.000Z' ); observeModerationPlayers( handle, 't6', [{ guid: '123', name: 'Delayed name' }], '2026-09-30T00:00:00.000Z' ); handle.prepare('DELETE FROM leaderboard_players').run(); expect(handle.prepare('SELECT * FROM moderation_players').get()).toMatchObject({ game: 't6', guid: '123', name: 'New name', first_seen_at: '2026-09-30T00:00:00.000Z', last_seen_at: '2026-10-02T00:00:00.000Z' }); expect(handle.prepare('SELECT name FROM moderation_aliases ORDER BY name').all()).toEqual([ { name: 'Delayed name' }, { name: 'New name' }, { name: 'Old name' } ]); }); it('records mutation results atomically and rejects reused keys or revoked access', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const { moderationMutation, moderationEvent } = await import('../../src/lib/server/moderation/core.js'); const handle = db(); handle .prepare( `INSERT INTO auth_users(id,username,display_name,password_hash,role,created_at,updated_at) VALUES('moderator','moderator','Moderator','unused','moderator',0,0)` ) .run(); const body = { op: 'note', idempotencyKey: 'request-1', text: 'A note' }; const work = vi.fn((actor, now) => { moderationEvent(actor, 'note', { game: 't5', guid: '123' }, null, 'A note', now); return { id: 'one' }; }); expect(moderationMutation('moderator', body, work)).toEqual({ ok: true, id: 'one' }); expect(moderationMutation('moderator', body, work)).toEqual({ ok: true, id: 'one' }); expect(work).toHaveBeenCalledTimes(1); expect(() => moderationMutation('moderator', { ...body, text: 'Changed' }, work)).toThrow( 'already used' ); expect(() => moderationMutation('moderator', { ...body, idempotencyKey: 'failing' }, (actor, now) => { moderationEvent(actor, 'note', {}, null, 'Rolled back', now); throw new Error('Failed'); }) ).toThrow('Failed'); expect(handle.prepare('SELECT COUNT(*) AS count FROM moderation_events').get()).toEqual({ count: 1 }); handle.prepare("UPDATE auth_users SET role='viewer' WHERE id='moderator'").run(); expect(() => moderationMutation('moderator', body, work)).toThrow('Moderation access'); });