import { afterEach, describe, expect, it, vi } from 'vitest' import { adaptEvent, adaptPlayer, adaptSeries, api, type RawEvent, type RawPlayer, type RawSeries } from './client' const player: RawPlayer = { id: 'player-1', accountId: 'account-1', displayName: 'Nova', ratings: { tank: 36, damage: 28, support: 24 }, preferredRoles: ['Tank'], preferredPlayerIds: ['player-2'], createdAt: '2026-07-01T00:00:00Z', updatedAt: '2026-07-18T00:00:00Z', } afterEach(() => vi.restoreAllMocks()) describe('backend DTO adapters', () => { it('adapts backend role ratings without changing their values', () => { expect(adaptPlayer(player)).toMatchObject({ displayName: 'Nova', ratings: { tank: 36, damage: 28, support: 24 }, preferredRoles: ['tank'], preferredPlayerIds: ['player-2'], }) }) it('adapts backend RSVP values and counts', () => { const event: RawEvent = { id: 'event-1', name: 'Scrim', description: '', startsAt: '2099-07-20T19:00:00Z', endsAt: '2099-07-20T23:00:00Z', registrationDeadline: '2099-07-20T18:00:00Z', createdBy: 'account-1', createdAt: '2026-07-01T00:00:00Z', updatedAt: '2026-07-01T00:00:00Z', } const adapted = adaptEvent(event, [ { eventId: event.id, playerId: player.id, actorAccountId: 'account-1', status: 'Going', source: 'player', updatedAt: event.updatedAt }, ], player.id) expect(adapted.myRsvp).toBe('going') expect(adapted.counts.going).toBe(1) }) it('keeps corrected results out of the authoritative score', () => { const series: RawSeries = { id: 'series-1', tournamentId: 't-1', teamAId: 'a', teamBId: 'b', winnerTeamId: '', bestOf: 3, version: 2, results: [ { mapName: 'Lijiang', outcome: 'TeamAWin', actorAccountId: 'admin', recordedAt: '2026-07-18T00:00:00Z' }, { mapName: 'Lijiang', outcome: 'TeamBWin', actorAccountId: 'admin', recordedAt: '2026-07-18T00:01:00Z', correctionOf: 1 }, ], } expect(adaptSeries(series).score).toEqual({ alpha: 0, beta: 1 }) }) }) describe('actual backend routes', () => { it('updates the current player through PATCH /api/me/player', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response(JSON.stringify(player), { status: 200, headers: { 'Content-Type': 'application/json' }, })) await api.updateRatings('Nova', { tank: 37, damage: 29, support: 25 }, ['tank', 'damage'], ['player-2']) expect(fetchMock).toHaveBeenCalledWith('/api/me/player', expect.objectContaining({ method: 'PATCH' })) expect(fetchMock.mock.calls[0][1]?.body).toContain('"preferredRoles":["Tank","Damage"]') }) it('sends backend RSVP enum values to the player-specific route', async () => { const responses = [ { eventId: 'event-1', playerId: 'player-1', actorAccountId: 'account-1', status: 'Going', source: 'player', updatedAt: '2026-07-18T00:00:00Z' }, [player], { account: { id: 'account-1', discordId: 'discord', username: 'Nova', avatarUrl: '', role: 'player', createdAt: '2026-07-01T00:00:00Z' }, player }, ] const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async () => new Response(JSON.stringify(responses.shift()), { status: 200, headers: { 'Content-Type': 'application/json' }, })) await api.setRsvp('event-1', 'player-1', 'going') const rsvpCall = fetchMock.mock.calls.find(([url]) => url === '/api/events/event-1/rsvps/player-1') expect(rsvpCall?.[1]?.body).toBe(JSON.stringify({ status: 'Going' })) }) })