import { acceptedCookie } from './terms-fixture.js'; import { it, expect } from 'vitest'; import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { randomUUID } from 'node:crypto'; import { io, type Socket } from 'socket.io-client'; import { FileStore } from '../server/store.mjs'; import { createService } from '../server/service.mjs'; import type { Room } from '../src/lib/types.js'; it('dos clientes reales: registro, sala, chat, jugadas, reconexión y resultado', async () => { const dir = await mkdtemp(join(tmpdir(), 'mesa-socket-')); const store = new FileStore(join(dir, 'store.json')); await store.init(); const origin = 'http://localhost:4173', service = createService(store, { APP_ORIGIN: origin, NODE_ENV: 'test' }); await new Promise((resolve) => service.server.listen(0, '127.0.0.1', resolve), ); const address = service.server.address(); if (!address || typeof address === 'string') throw new Error('Sin dirección'); const base = `http://127.0.0.1:${address.port}`, sockets: Socket[] = []; async function request( path: string, data?: unknown, cookie = '', customOrigin = origin, ) { return fetch(base + '/api' + path, { method: data === undefined ? 'GET' : 'POST', headers: { origin: customOrigin, 'content-type': 'application/json', cookie: acceptedCookie(cookie), }, body: data === undefined ? undefined : JSON.stringify(data), }); } async function player(n: number) { const response = await request('/auth/register', { name: `Prueba ${n}`, nick: `prueba_${n}`, email: `prueba${n}@example.test`, pin: '3581', }); expect(response.status).toBe(201); const cookie = response.headers.get('set-cookie')!.split(';')[0]; const socket = io(base, { transports: ['websocket'], extraHeaders: { cookie: acceptedCookie(cookie), origin }, forceNew: true, }); sockets.push(socket); await new Promise((resolve, reject) => { socket.once('connect', resolve); socket.once('connect_error', reject); }); return { cookie, socket }; } const emit = (socket: Socket, event: string, input: unknown) => new Promise<{ ok: boolean; room: Room; error?: string }>( (resolve, reject) => socket .timeout(4000) .emit( event, input, ( e: Error | null, result: { ok: boolean; room: Room; error?: string }, ) => (e ? reject(e) : resolve(result)), ), ); try { const a = await player(1), b = await player(2); expect( (await request('/rooms', {}, a.cookie, 'https://other.example')).status, ).toBe(403); const response = await request( '/rooms', { gameId: 'ajedrez', name: 'Partida de integración', maxPlayers: 2, visibility: 'private', }, a.cookie, ); let room = (await response.json()).room as Room; expect((await request('/rooms')).status).toBe(200); expect((await (await request('/rooms')).json()).rooms).toHaveLength(0); await request('/rooms/join', { code: room.code }, b.cookie); await emit(a.socket, 'room:subscribe', { roomId: room.id }); await emit(b.socket, 'room:subscribe', { roomId: room.id }); await emit(a.socket, 'room:ready', { roomId: room.id, ready: true }); room = ( await emit(b.socket, 'room:ready', { roomId: room.id, ready: true }) ).room; expect(room.status).toBe('playing'); const preset = await request( '/profile/avatar', { preset: 'gato' }, a.cookie, ); expect(preset.status).toBe(200); expect((await preset.json()).user.avatar).toBe('/avatars/gato.svg'); expect( (await (await request(`/rooms/${room.id}`, undefined, b.cookie)).json()) .room.players[0].avatar, ).toBe('/avatars/gato.svg'); expect((await request('/profile/avatar', { preset: 'torre' })).status).toBe( 401, ); expect( ( await request( '/profile/avatar', { image: 'data:image/svg+xml;base64,AAAA' }, a.cookie, ) ).status, ).toBe(400); const png = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4z8DwHwAFgAI/ScLbtAAAAABJRU5ErkJggg=='; const uploaded = await request('/profile/avatar', { image: png }, a.cookie); expect(uploaded.status).toBe(200); const avatarUrl = (await uploaded.json()).user.avatar; const served = await request(avatarUrl.slice(4), undefined, b.cookie); expect(served.headers.get('content-type')).toBe('image/png'); expect(Buffer.from(await served.arrayBuffer()).toString('base64')).toBe( png.slice(22), ); expect( (await emit(b.socket, 'room:subscribe', { roomId: room.id })).room .players[0].avatar, ).toBe(avatarUrl); expect( (await request('/profile/avatar', { preset: null }, a.cookie)).status, ).toBe(200); const chat = await emit(a.socket, 'chat:send', { roomId: room.id, text: 'Buena partida