import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import type Database from 'better-sqlite3'; import { newFixture, type Fixture } from '../helpers/fixture.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); }); afterEach(async () => fixture.cleanup()); function addMatches(handle: Database.Database, guid: string, count: number, { game = 't5', map = 'theater', kills = 10, name = guid } = {}) { handle.prepare(`INSERT OR IGNORE INTO leaderboard_players (game, guid, name, first_seen_at, last_seen_at) VALUES (?, ?, ?, '2026-09-01', '2026-09-02')`).run(game, guid, name); for (let i = 0; i < count; i++) { const inserted = handle.prepare(`INSERT INTO leaderboard_matches (server, game, map, map_key, map_name, started_at, ended_at, last_received_at, status, highest_round, last_event, last_fingerprint) VALUES ('removed-server', ?, ?, ?, ?, '2026-09-01', '2026-09-02', '2026-09-02', 'completed', 20, 'game_end', 'test')`) .run(game, map, `${game}:${map}`, map); handle.prepare(`INSERT INTO leaderboard_match_players (match_id, guid, highest_round, kills, headshots, downs, rounds_survived, completed, has_stats, first_seen_at, last_seen_at) VALUES (?, ?, 20, ?, 5, 1, 2, 1, 1, '2026-09-01', '2026-09-02')`).run(inserted.lastInsertRowid, guid, kills); } } it('filters and paginates match history independently of lifetime stats, map records and best evidence', async () => { const { db } = await import('../../src/lib/server/db/index.js'); addMatches(db(), '101', 30); addMatches(db(), '101', 3, { game: 't6', map: 'town', kills: 100 }); const { getPlayerProfile } = await import('../../src/lib/server/fleet/player-profile.js'); const lifetime = getPlayerProfile('101')!; expect(lifetime.matchesTotal).toBe(33); const first = getPlayerProfile('101', 0, { game: 't5', map: 't5:theater' })!; const second = getPlayerProfile('101', 25, { game: 't5', map: 't5:theater' })!; expect(first.matchesTotal).toBe(30); expect(first.matches).toHaveLength(25); expect(second.matches).toHaveLength(5); expect(new Set([...first.matches, ...second.matches].map(match => match.id)).size).toBe(30); expect(first.matches.every(match => match.game === 't5' && match.mapName === 'theater')).toBe(true); for (const profile of [first, second, getPlayerProfile('101', 0, { game: 't6' })!]) { expect(profile.stats).toEqual(lifetime.stats); expect(profile.maps).toEqual(lifetime.maps); expect(profile.records).toEqual(lifetime.records); } expect(getPlayerProfile('101', 999, { game: 't5' })!.offset).toBe(25); expect(getPlayerProfile('101', 999, { game: 't6' })).toMatchObject({ offset: 0, matchesTotal: 3 }); expect(getPlayerProfile('101', 999, { game: 't6', map: 't5:theater' })).toMatchObject({ stats: lifetime.stats, matches: [], matchesTotal: 0, offset: 0 }); for (const offset of [-1, NaN, 1.5, Infinity]) expect(getPlayerProfile('101', offset, { game: 't5' })!.offset).toBe(0); }); it('finds the exact GUID after combined or scoped ranking even beyond the first hundred name and GUID collisions', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const handle = db(); addMatches(handle, '101', 1, { kills: 10 }); addMatches(handle, '101', 1, { game: 't6', map: 'town', kills: 20 }); for (let index = 0; index < 105; index++) { addMatches(handle, `101-${index}`, 1, { kills: 100, name: '101 namesake' }); } const { getPlayerLeaderboardEntry, getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js'); expect(getLeaderboard({ q: '101', metric: 'kills', limit: 100 }).entries.some(entry => entry.guid === '101')).toBe(false); expect(getPlayerLeaderboardEntry('101', { metric: 'kills' })).toMatchObject({ guid: '101', rank: 106, gamesPlayed: 2, kills: 30, records: { bestKills: { game: 't6', value: 20 } } }); expect(getPlayerLeaderboardEntry('101', { metric: 'kills', game: 't5' })).toMatchObject({ guid: '101', rank: 106, gamesPlayed: 1, kills: 10, records: { bestKills: { game: 't5', value: 10 } } }); expect(getPlayerLeaderboardEntry('101', { metric: 'kills', game: 't6', map: 't6:town' })).toMatchObject({ guid: '101', rank: 1, gamesPlayed: 1, kills: 20 }); expect(getPlayerLeaderboardEntry('10', { metric: 'kills' })).toBeNull(); expect(getPlayerLeaderboardEntry("' OR 1=1 --", { metric: 'kills' })).toBeNull(); }); it('keeps lifetime profiles available when selected history is empty or an efficiency rank is unqualified', async () => { const { db } = await import('../../src/lib/server/db/index.js'); addMatches(db(), '101', 4); const { load } = await import('../../src/routes/leaderboard/players/[guid]/+page.server.js'); const filtered = load({ params: { guid: '101' }, url: new URL('http://localhost/leaderboard/players/101?game=t6&map=t6%3Atown&offset=25') }); expect(filtered).toMatchObject({ returnTo: '/leaderboard', profile: { stats: { gamesPlayed: 4, kills: 40 }, matches: [], matchesTotal: 0, offset: 0 }, scope: { game: 't6', map: 't6:town', metric: 'highestRound', entry: null } }); const unqualified = load({ params: { guid: '101' }, url: new URL('http://localhost/leaderboard/players/101?metric=averageKills') }); expect(unqualified).toMatchObject({ profile: { matchesTotal: 4, stats: { gamesPlayed: 4 } }, scope: { game: '', map: '', metric: 'averageKills', entry: null } }); addMatches(db(), '101', 1); expect(load({ params: { guid: '101' }, url: new URL('http://localhost/leaderboard/players/101?metric=averageKills') })) .toMatchObject({ scope: { entry: { guid: '101', rank: 1, averageKills: 10, gamesPlayed: 5 } } }); }); it('loads canonical navigation context while keeping source search and pagination out of profile rank', async () => { const { db } = await import('../../src/lib/server/db/index.js'); addMatches(db(), '101', 1, { kills: 100 }); addMatches(db(), '202', 1, { kills: 200 }); const { load } = await import('../../src/routes/leaderboard/players/[guid]/+page.server.js'); const url = new URL('http://localhost/leaderboard/players/101?game=t5&map=t5%3Atheater&metric=kills'); url.searchParams.set('returnTo', '/leaderboard?game=t5&map=t5%3Atheater&metric=kills&q=101&offset=50&unknown=drop'); const loaded = load({ params: { guid: '101' }, url }); expect(loaded.returnTo).toBe('/leaderboard?game=t5&map=t5%3Atheater&metric=kills&q=101&offset=50'); expect(loaded.scope).toMatchObject({ game: 't5', map: 't5:theater', metric: 'kills', entry: { guid: '101', rank: 2 } }); expect(loaded.profile.offset).toBe(0); url.searchParams.set('returnTo', 'https://external.invalid/leaderboard'); expect(load({ params: { guid: '101' }, url }).returnTo).toBe('/leaderboard'); const defaults = load({ params: { guid: '101' }, url: new URL('http://localhost/leaderboard/players/101') }); expect(defaults).toMatchObject({ returnTo: '/leaderboard', scope: { game: '', map: '', metric: 'highestRound' } }); }); it('rejects invalid filter parameters and retains existing offset normalization and missing-player behavior', async () => { const { db } = await import('../../src/lib/server/db/index.js'); addMatches(db(), '101', 30); const { load } = await import('../../src/routes/leaderboard/players/[guid]/+page.server.js'); for (const query of ['game=t7', 'game=', 'metric=', 'metric=unknown', `map=${'x'.repeat(1025)}`]) { expect(() => load({ params: { guid: '101' }, url: new URL(`http://localhost/leaderboard/players/101?${query}`) })) .toThrow(expect.objectContaining({ status: 400 })); } for (const query of ['offset=garbage', 'offset=-1', 'offset=1.5', 'offset=Infinity']) { expect(load({ params: { guid: '101' }, url: new URL(`http://localhost/leaderboard/players/101?${query}`) }).profile.offset).toBe(0); } expect(load({ params: { guid: '101' }, url: new URL('http://localhost/leaderboard/players/101?offset=999') }).profile.offset).toBe(25); expect(() => load({ params: { guid: 'missing' }, url: new URL('http://localhost/leaderboard/players/missing?game=t5') })) .toThrow(expect.objectContaining({ status: 404 })); });