import { describe, expect, it, vi } from 'vitest'; import { createAngularApiClient, type AngularHttpClientLike } from '../../../src/api/angular-client'; describe('SPA Angular API contract smoke (host/player foundation)', () => { it('smoke-checks canonical host/player endpoint contracts', async () => { const get = vi.fn(async (url: string) => { if (url === '/lobby/sessions/ABCD12') { return { session: { code: 'ABCD12', status: 'lobby', host_id: 1, current_round: 1, players_count: 2 }, players: [ { id: 2, nickname: 'Maja', score: 0, is_connected: true }, { id: 3, nickname: 'Bo', score: 0, is_connected: true } ], round_question: { id: 77, round_number: 1, prompt: 'Q?', shown_at: '2026-03-01T18:00:00Z', answers: [{ text: 'A' }, { text: 'B' }] }, reveal: { round_question_id: 77, round_number: 1, prompt: 'Q?', correct_answer: 'A', lies: [{ player_id: 2, nickname: 'Maja', text: 'B', created_at: '2026-03-01T18:00:05Z' }], guesses: [ { player_id: 3, nickname: 'Bo', selected_text: 'B', is_correct: false, fooled_player_id: 2, fooled_player_nickname: 'Maja', created_at: '2026-03-01T18:00:15Z' } ] }, phase_view_model: { status: 'lobby', round_number: 1, players_count: 2, constraints: { min_players_to_start: 2, max_players_mvp: 8, min_players_reached: true, max_players_allowed: true }, host: { can_start_round: true, can_show_question: false, can_mix_answers: false, can_calculate_scores: false, can_reveal_scoreboard: false, can_start_next_round: false, can_finish_game: false }, player: { can_join: true, can_submit_lie: false, can_submit_guess: false, can_view_final_result: false } } } as T; } if (url === '/lobby/sessions/ABCD12/scoreboard') { return { session: { code: 'ABCD12', status: 'scoreboard', current_round: 1 }, leaderboard: [ { id: 9, nickname: 'Maja', score: 200 }, { id: 10, nickname: 'Bo', score: 150 } ] } as T; } throw { status: 404, error: { error: 'Not found' } }; }); const post = vi.fn(async (url: string, body: unknown) => { if (url === '/lobby/sessions/join') { expect(body).toEqual({ code: 'ABCD12', nickname: 'Maja' }); return { player: { id: 9, nickname: 'Maja', session_token: 'session-token-1', score: 0 }, session: { code: 'ABCD12', status: 'lobby' } } as T; } if (url === '/lobby/sessions/ABCD12/rounds/start') { expect(body).toEqual({ category_slug: 'history' }); return { session: { code: 'ABCD12', status: 'lie', current_round: 1 }, round: { number: 1, category: { slug: 'history', name: 'History' } } } as T; } if (url === '/lobby/sessions/ABCD12/questions/show') { expect(body).toEqual({}); return { round_question: { id: 77, prompt: 'Q?', round_number: 1, shown_at: '2026-03-01T18:00:00Z', lie_deadline_at: '2026-03-01T18:00:30Z' }, config: { lie_seconds: 30 } } as T; } if (url === '/lobby/sessions/ABCD12/questions/77/answers/mix') { expect(body).toEqual({}); return { session: { code: 'ABCD12', status: 'guess', current_round: 1 }, round_question: { id: 77, round_number: 1 }, answers: [{ text: 'A' }, { text: 'B' }] } as T; } if (url === '/lobby/sessions/ABCD12/questions/77/scores/calculate') { expect(body).toEqual({}); return { session: { code: 'ABCD12', status: 'scoreboard', current_round: 1 }, round_question: { id: 77, round_number: 1 }, events_created: 2, reveal: { round_question_id: 77, correct_answer: 'A', lies: [], guesses: [] }, leaderboard: [ { id: 9, nickname: 'Maja', score: 200 }, { id: 10, nickname: 'Bo', score: 150 } ] } as T; } if (url === '/lobby/sessions/ABCD12/rounds/next') { expect(body).toEqual({}); return { session: { code: 'ABCD12', status: 'lobby', current_round: 2 } } as T; } if (url === '/lobby/sessions/ABCD12/finish') { expect(body).toEqual({}); return { session: { code: 'ABCD12', status: 'finished', current_round: 2 }, winner: { id: 9, nickname: 'Maja', score: 250 }, leaderboard: [ { id: 9, nickname: 'Maja', score: 250 }, { id: 10, nickname: 'Bo', score: 150 } ] } as T; } if (url === '/lobby/sessions/ABCD12/questions/77/lies/submit') { expect(body).toEqual({ player_id: 9, session_token: 'session-token-1', text: 'my lie' }); return { lie: { id: 501, player_id: 9, round_question_id: 77, text: 'my lie', created_at: '2026-03-01T18:00:05Z' }, window: { lie_deadline_at: '2026-03-01T18:00:30Z' } } as T; } if (url === '/lobby/sessions/ABCD12/questions/77/guesses/submit') { expect(body).toEqual({ player_id: 9, session_token: 'session-token-1', selected_text: 'B' }); return { guess: { id: 601, player_id: 9, round_question_id: 77, selected_text: 'B', is_correct: false, fooled_player_id: null, created_at: '2026-03-01T18:00:15Z' }, window: { guess_deadline_at: '2026-03-01T18:01:00Z' } } as T; } throw { status: 404, error: { error: 'Not found' } }; }); const client = createAngularApiClient({ get, post } as AngularHttpClientLike); const session = await client.getSession(' abcd12 '); expect(session.ok).toBe(true); if (session.ok) { expect(session.data.session.code).toBe('ABCD12'); expect(session.data.phase_view_model.host.can_start_next_round).toBe(false); expect(session.data.phase_view_model.player.can_submit_guess).toBe(false); expect(session.data.reveal?.correct_answer).toBe('A'); expect(session.data.reveal?.guesses[0].fooled_player_nickname).toBe('Maja'); } expect((await client.joinSession({ code: ' abcd12 ', nickname: ' Maja ' })).ok).toBe(true); expect((await client.startRound(' abcd12 ', { category_slug: 'history' })).ok).toBe(true); expect((await client.showQuestion(' abcd12 ')).ok).toBe(true); expect((await client.mixAnswers(' abcd12 ', 77)).ok).toBe(true); expect((await client.calculateScores(' abcd12 ', 77)).ok).toBe(true); expect((await client.getScoreboard(' abcd12 ')).ok).toBe(true); expect((await client.startNextRound(' abcd12 ')).ok).toBe(true); expect((await client.finishGame(' abcd12 ')).ok).toBe(true); expect( ( await client.submitLie(' abcd12 ', 77, { player_id: 9, session_token: 'session-token-1', text: 'my lie' }) ).ok ).toBe(true); expect( ( await client.submitGuess(' abcd12 ', 77, { player_id: 9, session_token: 'session-token-1', selected_text: 'B' }) ).ok ).toBe(true); expect(get).toHaveBeenNthCalledWith(1, '/lobby/sessions/ABCD12', { withCredentials: true }); expect(get).toHaveBeenNthCalledWith(2, '/lobby/sessions/ABCD12/scoreboard', { withCredentials: true }); expect(post).toHaveBeenNthCalledWith( 1, '/lobby/sessions/join', { code: 'ABCD12', nickname: 'Maja' }, { withCredentials: true } ); expect(post).toHaveBeenNthCalledWith( 2, '/lobby/sessions/ABCD12/rounds/start', { category_slug: 'history' }, { withCredentials: true } ); expect(post).toHaveBeenNthCalledWith(3, '/lobby/sessions/ABCD12/questions/show', {}, { withCredentials: true }); expect(post).toHaveBeenNthCalledWith( 4, '/lobby/sessions/ABCD12/questions/77/answers/mix', {}, { withCredentials: true } ); expect(post).toHaveBeenNthCalledWith( 5, '/lobby/sessions/ABCD12/questions/77/scores/calculate', {}, { withCredentials: true } ); expect(post).toHaveBeenNthCalledWith(6, '/lobby/sessions/ABCD12/rounds/next', {}, { withCredentials: true }); expect(post).toHaveBeenNthCalledWith(7, '/lobby/sessions/ABCD12/finish', {}, { withCredentials: true }); expect(post).toHaveBeenNthCalledWith( 8, '/lobby/sessions/ABCD12/questions/77/lies/submit', { player_id: 9, session_token: 'session-token-1', text: 'my lie' }, { withCredentials: true } ); expect(post).toHaveBeenNthCalledWith( 9, '/lobby/sessions/ABCD12/questions/77/guesses/submit', { player_id: 9, session_token: 'session-token-1', selected_text: 'B' }, { withCredentials: true } ); }); });