Files
playuniare.sori.studio/server/integration-test.js

161 lines
4.5 KiB
JavaScript

import assert from 'node:assert/strict'
import { io } from 'socket.io-client'
const SOCKET_SERVER_PORT = process.env.SOCKET_SERVER_PORT || '31873'
const SOCKET_SERVER_URL = `http://127.0.0.1:${SOCKET_SERVER_PORT}`
/**
* 테스트에 사용할 유효한 50장 덱 URL을 만든다.
*
* @returns {string} 덱 등록용 결과 페이지 URL
*/
function createValidDeckUrl() {
const cards = Array.from({ length: 13 }, (_, cardIndex) => ({
imgSrc: `/ua01/TEST-${String(cardIndex + 1).padStart(3, '0')}.png`,
Number: `TEST-${String(cardIndex + 1).padStart(3, '0')}`,
count: cardIndex === 12 ? 2 : 4,
color: 'Blue',
trigger: null,
}))
const deckUrl = new URL('http://uniare.sori.studio/result.html')
deckUrl.searchParams.set('series', 'ua01')
deckUrl.searchParams.set('seriesName', 'Code Geass')
deckUrl.searchParams.set('cards', JSON.stringify(cards))
return deckUrl.toString()
}
/**
* 연결 완료 또는 오류를 Promise로 기다린다.
*
* @param {import('socket.io-client').Socket} socket 확인할 클라이언트 소켓
* @returns {Promise<void>} 연결 완료 Promise
*/
function waitForConnection(socket) {
return new Promise((resolve, reject) => {
socket.once('connect', resolve)
socket.once('connect_error', reject)
})
}
/**
* Socket.IO 승인 응답이 있는 이벤트를 전송한다.
*
* @param {import('socket.io-client').Socket} socket 이벤트를 전송할 소켓
* @param {string} eventName 이벤트 이름
* @param {unknown[]} eventArguments 이벤트 인자
* @returns {Promise<object>} 서버 승인 응답
*/
function emitWithAcknowledgement(socket, eventName, ...eventArguments) {
return new Promise((resolve, reject) => {
socket.timeout(3000).emit(
eventName,
...eventArguments,
(timeoutError, response) => {
if (timeoutError) {
reject(timeoutError)
return
}
resolve(response)
},
)
})
}
/**
* 게임 시작 이벤트를 한 번 기다린다.
*
* @param {import('socket.io-client').Socket} socket 이벤트를 기다릴 소켓
* @returns {Promise<object>} 게임 시작 정보
*/
function waitForGameStart(socket) {
return new Promise((resolve) => {
socket.once('game:started', resolve)
})
}
/**
* 두 클라이언트의 방 생성부터 게임 시작까지 검증한다.
*
* @returns {Promise<void>} 검증 완료 Promise
*/
async function runIntegrationTest() {
const firstPlayerSocket = io(SOCKET_SERVER_URL, {
path: '/socket.io',
transports: ['websocket', 'polling'],
})
const secondPlayerSocket = io(SOCKET_SERVER_URL, {
path: '/socket.io',
transports: ['websocket', 'polling'],
})
try {
await Promise.all([
waitForConnection(firstPlayerSocket),
waitForConnection(secondPlayerSocket),
])
const createRoomResponse = await emitWithAcknowledgement(
firstPlayerSocket,
'room:create',
)
assert.equal(createRoomResponse.ok, true)
assert.equal(createRoomResponse.room.players.length, 1)
const joinRoomResponse = await emitWithAcknowledgement(
secondPlayerSocket,
'room:join',
createRoomResponse.room.roomCode,
)
assert.equal(joinRoomResponse.ok, true)
assert.equal(joinRoomResponse.room.players.length, 2)
const validDeckUrl = createValidDeckUrl()
const firstDeckResponse = await emitWithAcknowledgement(
firstPlayerSocket,
'player:register-deck',
validDeckUrl,
)
const secondDeckResponse = await emitWithAcknowledgement(
secondPlayerSocket,
'player:register-deck',
validDeckUrl,
)
assert.equal(firstDeckResponse.ok, true)
assert.equal(secondDeckResponse.ok, true)
const firstGameStart = waitForGameStart(firstPlayerSocket)
const secondGameStart = waitForGameStart(secondPlayerSocket)
await emitWithAcknowledgement(
firstPlayerSocket,
'player:set-ready',
true,
)
await emitWithAcknowledgement(
secondPlayerSocket,
'player:set-ready',
true,
)
const [firstStartPayload, secondStartPayload] = await Promise.all([
firstGameStart,
secondGameStart,
])
assert.equal(firstStartPayload.seat, 'player-one')
assert.equal(secondStartPayload.seat, 'player-two')
assert.equal(firstStartPayload.roomCode, secondStartPayload.roomCode)
assert.match(firstStartPayload.gameQuery, /series=ua01/)
console.log('Socket.IO 2인 방 통합 테스트를 통과했습니다.')
} finally {
firstPlayerSocket.disconnect()
secondPlayerSocket.disconnect()
}
}
await runIntegrationTest()