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
+175 -54
View File
@@ -4,31 +4,59 @@ import type { LeaderboardMetric } from '../../src/lib/leaderboard.js';
import { newFixture, type Fixture } from '../helpers/fixture.js';
let fixture: Fixture;
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
beforeEach(async () => {
vi.resetModules();
fixture = await newFixture([]);
});
afterEach(async () => fixture.cleanup());
interface Sample {
kills?: number; headshots?: number; downs?: number; rounds?: number; completed?: number;
game?: string; map?: string; status?: 'active' | 'completed' | 'interrupted';
kills?: number;
headshots?: number;
downs?: number;
rounds?: number;
completed?: number;
game?: string;
map?: string;
status?: 'active' | 'completed' | 'interrupted';
}
function addSamples(handle: Database.Database, guid: string, samples: Sample[]) {
for (const sample of samples) {
const { kills = 20, headshots = 10, downs = 1, rounds = 2, completed = 1,
game = 't5', map = 'theater', status = 'completed' } = sample;
const inserted = handle.prepare(`INSERT INTO leaderboard_matches
const {
kills = 20,
headshots = 10,
downs = 1,
rounds = 2,
completed = 1,
game = 't5',
map = 'theater',
status = 'completed'
} = sample;
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', ?, 20, 'game_end', 'test')`)
VALUES ('removed-server', ?, ?, ?, ?, '2026-09-01', '2026-09-02', '2026-09-02', ?, 20, 'game_end', 'test')`
)
.run(game, map, `${game}:${map}`, map, status);
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, guid);
handle.prepare(`INSERT INTO leaderboard_match_players (match_id, guid, highest_round, kills, headshots,
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, guid);
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, ?, ?, ?, ?, ?, 1, '2026-09-01', '2026-09-02')`)
VALUES (?, ?, 20, ?, ?, ?, ?, ?, 1, '2026-09-01', '2026-09-02')`
)
.run(inserted.lastInsertRowid, guid, kills, headshots, downs, rounds, completed);
}
}
const samples = (count: number, sample: Sample = {}) => Array.from({ length: count }, () => ({ ...sample }));
const samples = (count: number, sample: Sample = {}) =>
Array.from({ length: count }, () => ({ ...sample }));
it('calculates every efficiency measure from aggregate counts and preserves raw ratios', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
@@ -39,10 +67,21 @@ it('calculates every efficiency measure from aggregate counts and preserves raw
]);
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
const entry = getLeaderboard().entries[0];
expect(entry).toMatchObject({ kills: 100, headshots: 10, downs: 10, roundsSurvived: 10,
gamesPlayed: 5, gamesCompleted: 1, headshotRate: 0.1, killsPerDown: 10,
averageKills: 20, averageRounds: 2, completionRate: 0.2 });
const { efficiencyMetrics, efficiencyValue } = await import('../../src/lib/leaderboard-efficiency.js');
expect(entry).toMatchObject({
kills: 100,
headshots: 10,
downs: 10,
roundsSurvived: 10,
gamesPlayed: 5,
gamesCompleted: 1,
headshotRate: 0.1,
killsPerDown: 10,
averageKills: 20,
averageRounds: 2,
completionRate: 0.2
});
const { efficiencyMetrics, efficiencyValue } =
await import('../../src/lib/leaderboard-efficiency.js');
for (const { key } of efficiencyMetrics) {
expect(entry[key]).toBe(efficiencyValue(entry, key));
expect(getLeaderboard({ metric: key }).entries).toEqual([entry]);
@@ -57,7 +96,9 @@ it('qualifies at five games, one hundred kills and five downs, with each metric
addSamples(db(), 'four-downs', [{ downs: 0 }, ...samples(4)]);
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
const ids = (metric: LeaderboardMetric) =>
getLeaderboard({ metric }).entries.map(entry => entry.guid).sort();
getLeaderboard({ metric })
.entries.map((entry) => entry.guid)
.sort();
expect(ids('headshotRate')).toEqual(['exact', 'four-downs']);
expect(ids('killsPerDown')).toEqual(['exact', 'ninety-nine-kills']);
for (const metric of ['averageKills', 'averageRounds', 'completionRate'] as const) {
@@ -69,16 +110,31 @@ it('qualifies at five games, one hundred kills and five downs, with each metric
it('keeps undefined ratios null and unranked while ranking legitimate zero efficiencies', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
addSamples(db(), 'zero', samples(5, { kills: 0, headshots: 0, downs: 0, rounds: 0, completed: 0 }));
addSamples(
db(),
'zero',
samples(5, { kills: 0, headshots: 0, downs: 0, rounds: 0, completed: 0 })
);
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
expect(getLeaderboard().entries[0]).toMatchObject({ headshotRate: null, killsPerDown: null,
averageKills: 0, averageRounds: 0, completionRate: 0 });
expect(getLeaderboard().entries[0]).toMatchObject({
headshotRate: null,
killsPerDown: null,
averageKills: 0,
averageRounds: 0,
completionRate: 0
});
for (const metric of ['headshotRate', 'killsPerDown'] as const) {
expect(getLeaderboard({ metric })).toMatchObject({ entries: [], total: 0,
summary: { players: 1, matches: 5, kills: 0 } });
expect(getLeaderboard({ metric })).toMatchObject({
entries: [],
total: 0,
summary: { players: 1, matches: 5, kills: 0 }
});
}
for (const metric of ['averageKills', 'averageRounds', 'completionRate'] as const) {
expect(getLeaderboard({ metric })).toMatchObject({ total: 1, entries: [{ guid: 'zero', rank: 1 }] });
expect(getLeaderboard({ metric })).toMatchObject({
total: 1,
entries: [{ guid: 'zero', rank: 1 }]
});
}
addSamples(db(), 'no-kills', samples(5, { kills: 0, headshots: 0, downs: 1 }));
expect(getLeaderboard({ metric: 'killsPerDown' }).entries).toEqual([
@@ -89,19 +145,31 @@ it('keeps undefined ratios null and unranked while ranking legitimate zero effic
it('recomputes qualification within selected games and maps using completed matches only', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
addSamples(db(), 'split', [...samples(3), ...samples(2, { game: 't6', map: 'town' })]);
addSamples(db(), 'unfinished', [...samples(4), { status: 'active', kills: 10000 },
{ status: 'interrupted', kills: 10000 }]);
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
expect(getLeaderboard({ metric: 'averageKills' })).toMatchObject({ total: 1,
entries: [{ guid: 'split', gamesPlayed: 5, averageKills: 20 }], summary: { players: 2, matches: 9, kills: 180 } });
expect(getLeaderboard({ metric: 'averageKills', game: 't5' })).toMatchObject({ total: 0, entries: [],
summary: { players: 2, matches: 7, kills: 140 } });
expect(getLeaderboard({ metric: 'averageKills', map: 't6:town' })).toMatchObject({ total: 0, entries: [],
summary: { players: 1, matches: 2, kills: 40 } });
addSamples(db(), 'split', samples(2));
expect(getLeaderboard({ metric: 'averageKills', game: 't5', map: 't5:theater' }).entries).toEqual([
expect.objectContaining({ guid: 'split', gamesPlayed: 5, rank: 1 })
addSamples(db(), 'unfinished', [
...samples(4),
{ status: 'active', kills: 10000 },
{ status: 'interrupted', kills: 10000 }
]);
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
expect(getLeaderboard({ metric: 'averageKills' })).toMatchObject({
total: 1,
entries: [{ guid: 'split', gamesPlayed: 5, averageKills: 20 }],
summary: { players: 2, matches: 9, kills: 180 }
});
expect(getLeaderboard({ metric: 'averageKills', game: 't5' })).toMatchObject({
total: 0,
entries: [],
summary: { players: 2, matches: 7, kills: 140 }
});
expect(getLeaderboard({ metric: 'averageKills', map: 't6:town' })).toMatchObject({
total: 0,
entries: [],
summary: { players: 1, matches: 2, kills: 40 }
});
addSamples(db(), 'split', samples(2));
expect(
getLeaderboard({ metric: 'averageKills', game: 't5', map: 't5:theater' }).entries
).toEqual([expect.objectContaining({ guid: 'split', gamesPlayed: 5, rank: 1 })]);
});
it('qualifies before competition ranking, then searches and paginates without changing scope summaries', async () => {
@@ -111,22 +179,59 @@ it('qualifies before competition ranking, then searches and paginates without ch
addSamples(handle, 'Blake', samples(5, { kills: 50 }));
addSamples(handle, 'Casey', samples(5, { kills: 10 }));
addSamples(handle, 'Small sample', samples(4, { kills: 1000 }));
handle.prepare(`INSERT INTO leaderboard_players (game, guid, name, first_seen_at, last_seen_at)
VALUES ('t6', 'Casey', 'Current name', '2026-09-01', '2026-09-03')`).run();
handle
.prepare(
`INSERT INTO leaderboard_players (game, guid, name, first_seen_at, last_seen_at)
VALUES ('t6', 'Casey', 'Current name', '2026-09-01', '2026-09-03')`
)
.run();
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
const full = getLeaderboard({ metric: 'averageKills', game: 't5' });
expect(full.entries.map(entry => [entry.name, entry.rank])).toEqual([['Ace', 1], ['Blake', 1], ['Current name', 3]]);
const searched = getLeaderboard({ metric: 'averageKills', game: 't5', q: 'current NAME', limit: 1 });
expect(searched).toMatchObject({ total: 1, entries: [{ guid: 'Casey', rank: 3 }], summary: full.summary });
expect(getLeaderboard({ metric: 'averageKills', game: 't5', q: 'Small' })).toMatchObject({ total: 0, entries: [], summary: full.summary });
expect(full.entries.map((entry) => [entry.name, entry.rank])).toEqual([
['Ace', 1],
['Blake', 1],
['Current name', 3]
]);
const searched = getLeaderboard({
metric: 'averageKills',
game: 't5',
q: 'current NAME',
limit: 1
});
expect(searched).toMatchObject({
total: 1,
entries: [{ guid: 'Casey', rank: 3 }],
summary: full.summary
});
expect(getLeaderboard({ metric: 'averageKills', game: 't5', q: 'Small' })).toMatchObject({
total: 0,
entries: [],
summary: full.summary
});
const page = getLeaderboard({ metric: 'averageKills', game: 't5', limit: 1, offset: 1 });
expect(page).toMatchObject({ total: 3, entries: [{ guid: 'Blake', rank: 1 }], summary: full.summary });
expect(getLeaderboard({ metric: 'averageKills', offset: 3 })).toMatchObject({ total: 3, entries: [], summary: full.summary });
expect(page).toMatchObject({
total: 3,
entries: [{ guid: 'Blake', rank: 1 }],
summary: full.summary
});
expect(getLeaderboard({ metric: 'averageKills', offset: 3 })).toMatchObject({
total: 3,
entries: [],
summary: full.summary
});
const { GET } = await import('../../src/routes/api/leaderboard/+server.js');
const response = await GET({ url: new URL('http://localhost/api/leaderboard?metric=averageKills&game=t5&q=current&limit=1') });
const response = await GET({
url: new URL(
'http://localhost/api/leaderboard?metric=averageKills&game=t5&q=current&limit=1'
)
});
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({ metric: 'averageKills', total: 1,
entries: [{ guid: 'Casey', rank: 3, averageKills: 10, gamesPlayed: 5 }], summary: full.summary });
expect(await response.json()).toMatchObject({
metric: 'averageKills',
total: 1,
entries: [{ guid: 'Casey', rank: 3, averageKills: 10, gamesPlayed: 5 }],
summary: full.summary
});
});
it('accepts each efficiency metric through the API when the qualified group is empty', async () => {
@@ -135,21 +240,37 @@ it('accepts each efficiency metric through the API when the qualified group is e
const { efficiencyMetrics } = await import('../../src/lib/leaderboard-efficiency.js');
const { GET } = await import('../../src/routes/api/leaderboard/+server.js');
for (const { key } of efficiencyMetrics) {
const response = await GET({ url: new URL(`http://localhost/api/leaderboard?metric=${key}`) });
const response = await GET({
url: new URL(`http://localhost/api/leaderboard?metric=${key}`)
});
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({ metric: key, entries: [], total: 0,
summary: { players: 1, matches: 4, kills: 80 } });
expect(await response.json()).toMatchObject({
metric: key,
entries: [],
total: 0,
summary: { players: 1, matches: 4, kills: 80 }
});
}
});
it('does not use same-round departures to qualify for efficiency rankings', async () => {
const { db } = await import('../../src/lib/server/db/index.js');
addSamples(db(), 'short-visit', [...samples(4), { completed: 0 }]);
db().prepare(`UPDATE leaderboard_match_players SET first_observed_round = highest_round
WHERE completed = 0`).run();
db()
.prepare(
`UPDATE leaderboard_match_players SET first_observed_round = highest_round
WHERE completed = 0`
)
.run();
const { getLeaderboard } = await import('../../src/lib/server/fleet/leaderboard.js');
expect(getLeaderboard().entries[0]).toMatchObject({ gamesPlayed: 4, gamesCompleted: 4,
kills: 100, averageKills: 25, completionRate: 1 });
expect(getLeaderboard().entries[0]).toMatchObject({
gamesPlayed: 4,
gamesCompleted: 4,
kills: 100,
averageKills: 25,
completionRate: 1
});
const { efficiencyMetrics } = await import('../../src/lib/leaderboard-efficiency.js');
for (const { key } of efficiencyMetrics) expect(getLeaderboard({ metric: key }).entries).toEqual([]);
for (const { key } of efficiencyMetrics)
expect(getLeaderboard({ metric: key }).entries).toEqual([]);
});