import { describe, expect, it } from 'vitest'; import type { GameLibrary, GameLibraryGame, GameLibraryVersion } from '../../src/lib/game-files.js'; import { latestGameFiles, selectLatestGameFiles } from '../../src/lib/ui/native-game-files.js'; function upload( id: string, game: GameLibraryGame = 't5', state: GameLibraryVersion['state'] = 'ready' ): GameLibraryVersion { return { id, libraryId: game, game, version: id, description: '', state, sizeBytes: 10, fileCount: 1, manifestSha256: 'manifest', files: [], createdAt: '2026-10-04T12:00:00Z', publishedAt: state === 'ready' ? '2026-10-04T12:05:00Z' : null, storageLocation: '', sourceHostId: null, sourceVolume: null, error: null, operationId: null, caches: [] }; } function slot( game: GameLibraryGame, latestVersionId: string | null, versions: GameLibraryVersion[] ): GameLibrary { return { id: game, name: game.toUpperCase(), game, description: '', createdAt: '', latestVersionId, versions }; } describe('automatic game file selection for new launches', () => { it('follows the controller latest upload even when older files are already cached', () => { const old = upload('old'); old.caches = [ { hostId: 'host', libraryVersionId: old.id, status: 'ready', bytesDownloaded: 10, bytesTotal: 10, manifestSha256: 'manifest', error: null, updatedAt: '', operationId: null } ]; const current = upload('latest'); // The API can retain older uploads and unfinished uploads in its history. const libraries = [ slot('t5', current.id, [upload('unfinished', 't5', 'importing'), old, current]) ]; const selected = { t5: old.id }; const latest = latestGameFiles(libraries); expect(latest.t5).toBe(current); expect(selectLatestGameFiles(selected, latest, ['t5'])).toBe(true); expect(selected.t5).toBe(current.id); expect(selectLatestGameFiles(selected, latest, ['t5'])).toBe(false); }); it('updates both games and removes a stale selection when a game has no available upload', () => { const latest = latestGameFiles([ slot('t5', null, [upload('unavailable')]), slot('t6', 'new-t6', [upload('new-t6', 't6')]) ]); const selected: Partial> = { t5: 'old-t5', t6: 'old-t6' }; expect(selectLatestGameFiles(selected, latest, ['t5', 't6'])).toBe(true); expect(selected).toEqual({ t6: 'new-t6' }); }); it('never selects an unfinished or missing latest upload from history', () => { expect( latestGameFiles([ slot('t5', 'pending', [upload('old'), upload('pending', 't5', 'importing')]) ]) ).toEqual({}); expect(latestGameFiles([slot('t6', 'missing', [upload('old', 't6')])])).toEqual({}); }); it('only changes games needed by the current launch', () => { const selected = { t5: 'old-t5', t6: 'keep-t6' }; const latest = latestGameFiles([slot('t5', 'new-t5', [upload('new-t5')])]); expect(selectLatestGameFiles(selected, latest, ['t5'])).toBe(true); expect(selected).toEqual({ t5: 'new-t5', t6: 'keep-t6' }); }); });