import { describe, expect, it } from 'vitest' import { battleLabel, battleNumbers, detectBattles, isRaceSession } from '../lib/battles' import type { LiveTimingRow } from '../lib/live' import type { LiveDriverData } from '../types' function row( number: string, position: number, interval: string, tla: string, overrides: Partial = {}, ): LiveTimingRow { return { RacingNumber: number, Position: position, Driver: { RacingNumber: number, Position: position, Interval: interval, GapToLeader: interval, InPit: false, PitOut: false, Retired: false, ...overrides, } as LiveDriverData, Info: { RacingNumber: number, BroadcastName: '', Tla: tla, TeamName: '', TeamColour: '', FirstName: '', LastName: '', }, } } describe('isRaceSession', () => { it('only treats race and sprint sessions as races', () => { expect(isRaceSession('Race')).toBe(true) expect(isRaceSession('Sprint')).toBe(true) expect(isRaceSession('Sprint Qualifying')).toBe(false) expect(isRaceSession('Qualifying')).toBe(false) expect(isRaceSession('Practice')).toBe(false) expect(isRaceSession('')).toBe(false) expect(isRaceSession(undefined)).toBe(false) }) }) describe('detectBattles', () => { const rows = [ row('1', 1, '', 'VER'), row('4', 2, '+0.4', 'NOR'), row('81', 3, '+0.8', 'PIA'), row('16', 4, '+5.0', 'LEC'), row('44', 5, '+0.9', 'HAM'), row('63', 6, '+12.2', 'RUS'), ] it('groups consecutive cars within 1.0s in race sessions', () => { const battles = detectBattles(rows, 'Race') expect(battles).toHaveLength(2) expect(battles[0].drivers.map((d) => d.code)).toEqual(['VER', 'NOR', 'PIA']) expect(battles[0].minGap).toBeCloseTo(0.4) expect(battles[1].drivers.map((d) => d.code)).toEqual(['LEC', 'HAM']) expect(battles[1].minGap).toBeCloseTo(0.9) }) it('returns nothing outside race sessions', () => { expect(detectBattles(rows, 'Qualifying')).toEqual([]) expect(detectBattles(rows, undefined)).toEqual([]) }) it('excludes cars in the pits or retired', () => { const pitted = [ row('1', 1, '', 'VER'), row('4', 2, '+0.4', 'NOR', { InPit: true }), row('81', 3, '+0.6', 'PIA'), row('16', 4, '+8.0', 'LEC'), row('44', 5, '+0.5', 'HAM', { Retired: true }), ] const battles = detectBattles(pitted, 'Race') expect(battles).toEqual([]) }) it('ignores lapped and missing intervals', () => { const lapped = [ row('1', 1, '', 'VER'), row('4', 2, '1L', 'NOR'), row('81', 3, '', 'PIA'), ] expect(detectBattles(lapped, 'Race')).toEqual([]) }) it('handles empty and single-row input', () => { expect(detectBattles([], 'Race')).toEqual([]) expect(detectBattles([row('1', 1, '', 'VER')], 'Race')).toEqual([]) }) }) describe('battleLabel / battleNumbers', () => { it('formats a two-car chip label', () => { const battles = detectBattles([row('1', 1, '', 'VER'), row('4', 2, '+0.4', 'NOR')], 'Race') expect(battleLabel(battles[0])).toBe('VER ⚔ NOR +0.4') }) it('collects racing numbers across all battles', () => { const battles = detectBattles( [ row('1', 1, '', 'VER'), row('4', 2, '+0.4', 'NOR'), row('16', 3, '+9.0', 'LEC'), row('44', 4, '+0.7', 'HAM'), ], 'Race', ) expect(battleNumbers(battles)).toEqual(new Set(['1', '4', '16', '44'])) }) })