You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Juegoland/tests/room-lifecycle-service.test.ts

155 lines
5.3 KiB

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 { 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('HTTP exits and inactivity closure notify real sockets and update both profiles', async () => {
const directory = await mkdtemp(join(tmpdir(), 'juegoland-exit-service-'));
const store = new FileStore(join(directory, 'store.json'));
await store.init();
const origin = 'http://localhost:4173';
const service = createService(store, {
APP_ORIGIN: origin,
NODE_ENV: 'test',
});
await new Promise<void>((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}`;
const sockets: Socket[] = [];
const request = (path: string, cookie = '', data?: unknown) =>
fetch(base + '/api' + path, {
method: data === undefined ? 'GET' : 'POST',
headers: {
cookie: acceptedCookie(cookie),
origin,
'content-type': 'application/json',
},
body: data === undefined ? undefined : JSON.stringify(data),
});
async function guest(name: string) {
const response = await request('/auth/guest', '', { name });
return {
user: (await response.json()).user,
cookie: response.headers.get('set-cookie')!.split(';')[0],
};
}
async function socketFor(cookie: string, roomId: string) {
const socket = io(base, {
forceNew: true,
transports: ['websocket'],
extraHeaders: { cookie: acceptedCookie(cookie), origin },
});
sockets.push(socket);
await new Promise<void>((resolve, reject) => {
socket.once('connect', resolve);
socket.once('connect_error', reject);
});
const response = await socket
.timeout(4000)
.emitWithAck('room:subscribe', { roomId });
expect(response.ok).toBe(true);
return socket;
}
try {
const a = await guest('Anfitrión'),
b = await guest('Invitado'),
outsider = await guest('Fuera');
const room = (
await (
await request('/rooms', a.cookie, {
gameId: 'oca',
name: 'Sala para salir',
visibility: 'private',
maxPlayers: 2,
})
).json()
).room;
await request('/rooms/join', b.cookie, { code: room.code });
const one = await socketFor(a.cookie, room.id),
two = await socketFor(b.cookie, room.id);
const removed = new Promise<{ roomId: string }>((resolve) =>
two.once('room:removed', resolve),
);
const before = await service.platform.view(room.id, b.user.id);
expect(
(
await request(`/rooms/${room.id}/leave`, b.cookie, {
expectedVersion: before.version,
})
).status,
).toBe(200);
expect((await removed).roomId).toBe(room.id);
expect(
(await (await request('/profile', b.cookie)).json()).pending,
).toHaveLength(0);
expect((await request(`/rooms/${room.id}`, b.cookie)).status).toBe(400);
await request('/rooms/join', b.cookie, { code: room.code });
await two.timeout(4000).emitWithAck('room:subscribe', { roomId: room.id });
await service.platform.ready(a.user, room.id, true);
await service.platform.ready(b.user, room.id, true);
const playing = await service.platform.view(room.id, b.user.id);
expect(
(
await request(`/rooms/${room.id}/close-inactive`, b.cookie, {
expectedVersion: playing.version,
})
).status,
).toBe(400);
await store.updateRoom(
room.id,
(stored: any) =>
(stored.lastGameActivityAt = new Date(
Date.now() - 11 * 60_000,
).toISOString()),
);
expect(
(
await request(`/rooms/${room.id}/close-inactive`, outsider.cookie, {
expectedVersion: playing.version,
})
).status,
).toBe(400);
const finished = (socket: Socket) =>
new Promise<Room>((resolve) => {
socket.on('room:state', function listen(state: Room) {
if (state.status === 'finished') {
socket.off('room:state', listen);
resolve(state);
}
});
});
const notices = Promise.all([finished(one), finished(two)]);
expect(
(
await request(`/rooms/${room.id}/close-inactive`, b.cookie, {
expectedVersion: playing.version,
})
).status,
).toBe(200);
expect((await notices).every((state) => state.result?.cancelled)).toBe(
true,
);
for (const player of [a, b]) {
const profile = await (await request('/profile', player.cookie)).json();
expect(profile.pending).toHaveLength(0);
expect(profile.history[0].result.cancelled).toBe(true);
expect(
profile.stats.every((s: { played: number }) => s.played === 0),
).toBe(true);
}
} finally {
for (const socket of sockets) socket.disconnect();
await service.close();
await store.close();
await rm(directory, { recursive: true, force: true });
}
});

Powered by TurnKey Linux.