feat: Sync to OSS repo

This commit is contained in:
Wanjohi
2026-08-06 22:13:51 +03:00
parent 46d2a56180
commit 3faac3008f
144 changed files with 27561 additions and 0 deletions
+230
View File
@@ -0,0 +1,230 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import { testDb } from '../db/test.js';
import { Game } from '../game/index.js';
import { Identifier } from '../id.js';
import { GameDownload } from './download.js';
const sql = testDb();
const HOST_A = 'hst_aaaaaaaaaaaaaaaaaaaaaaaaa';
const HOST_B = 'hst_bbbbbbbbbbbbbbbbbbbbbbbbb';
const createdGameIds: string[] = [];
const gameIdByApp = new Map<number, string>();
async function ensureGame(steamAppId: number): Promise<string> {
const existing = gameIdByApp.get(steamAppId);
if (existing) return existing;
const [row] = await Game.upsert({
id: Identifier.ascending('game'),
steamAppId,
slug: `test-game-${steamAppId}`,
name: `Test Game ${steamAppId}`
});
if (!row) throw new Error('expected a game row');
createdGameIds.push(row.id);
gameIdByApp.set(steamAppId, row.id);
return row.id;
}
beforeAll(async () => {
await ensureGame(4400);
await ensureGame(4401);
await ensureGame(4402);
});
afterAll(async () => {
if (createdGameIds.length > 0) {
// Deleting the games cascades to their game_download rows.
await sql`delete from "game" where id in ${sql(createdGameIds)}`;
createdGameIds.length = 0;
}
});
describe('GameDownload', () => {
function expectRow<T>(row: T | null | undefined): T {
expect(row).not.toBeNull();
return row as T;
}
test('a new host/game creates one row', async () => {
const gameId = await ensureGame(4400);
const row = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading',
progressBytes: 1000,
totalBytes: 5000
})
);
expect(row.id).toMatch(/^gdl_/);
expect(row.hostId).toBe(HOST_A);
expect(row.gameId).toBe(gameId);
expect(row.status).toBe('downloading');
expect(row.timeStarted).toBeInstanceOf(Date);
});
test('repeating the same host/game updates one row', async () => {
const gameId = await ensureGame(4400);
const first = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading',
progressBytes: 1000,
totalBytes: 5000
})
);
const second = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading',
progressBytes: 2000,
totalBytes: 5000
})
);
expect(second.id).toBe(first.id);
expect(second.progressBytes).toBe(2000);
expect(second.totalBytes).toBe(5000);
const count =
await sql`select count(*)::int as n from "game_download" where host_id = ${HOST_A} and game_id = ${gameId}`;
expect(count[0]?.n).toBe(1);
});
test('reports for other users do not create another row', async () => {
const gameId = await ensureGame(4401);
// No user dimension exists on the shared state row: reports for any
// user land on the single (host, game) row.
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading',
progressBytes: 1
});
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading',
progressBytes: 2
});
const rows = await GameDownload.listByHost(HOST_A);
expect(rows.filter((r) => r.gameId === gameId).length).toBe(1);
});
test('two hosts create two independent rows', async () => {
const gameId = await ensureGame(4401);
const a = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'downloading'
})
);
const b = expectRow(
await GameDownload.upsertState({
hostId: HOST_B,
gameId,
status: 'downloading'
})
);
expect(a.id).not.toBe(b.id);
expect(a.hostId).toBe(HOST_A);
expect(b.hostId).toBe(HOST_B);
});
test('verifying is accepted', async () => {
const gameId = await ensureGame(4401);
const row = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'verifying',
progressBytes: 2048
})
);
expect(row.status).toBe('verifying');
expect(row.timeStarted).toBeInstanceOf(Date);
});
test('ready sets timeCompleted', async () => {
const gameId = await ensureGame(4400);
const row = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'ready',
progressBytes: 5000,
totalBytes: 5000
})
);
expect(row.status).toBe('ready');
expect(row.timeCompleted).toBeInstanceOf(Date);
expect(row.timeStarted).toBeInstanceOf(Date);
});
test('failed records an error', async () => {
const gameId = await ensureGame(4401);
const row = expectRow(
await GameDownload.upsertState({
hostId: HOST_A,
gameId,
status: 'failed',
errorMessage: 'depot key missing'
})
);
expect(row.status).toBe('failed');
expect(row.errorMessage).toBe('depot key missing');
});
test('progress updates do not require a user ID', async () => {
const gameId = await ensureGame(4402);
const row = expectRow(
await GameDownload.upsertState({
hostId: HOST_B,
gameId,
status: 'downloading',
progressBytes: 12345
})
);
expect(row.progressBytes).toBe(12345);
expect(row).not.toHaveProperty('userId');
});
test('old user_download table is no longer referenced', async () => {
const rows =
await sql`select table_name from information_schema.tables where table_schema = 'public' and table_name = 'user_download'`;
expect(rows.length).toBe(0);
});
test('findByHostAndGame and listByGame', async () => {
const gameId = await ensureGame(4402);
const found = await GameDownload.findByHostAndGame({ hostId: HOST_B, gameId });
expect(found).not.toBeNull();
expect(found!.gameId).toBe(gameId);
const byGame = await GameDownload.listByGame(gameId);
expect(byGame.some((r) => r.hostId === HOST_B)).toBe(true);
});
test('markReady and markFailed update the row', async () => {
const gameId = await ensureGame(4402);
const ready = await GameDownload.markReady({ hostId: HOST_B, gameId });
expect(ready!.status).toBe('ready');
expect(ready!.timeCompleted).toBeInstanceOf(Date);
const failed = await GameDownload.markFailed({
hostId: HOST_B,
gameId,
errorMessage: 'disk full'
});
expect(failed!.status).toBe('failed');
expect(failed!.errorMessage).toBe('disk full');
});
});