강동현

테스트 작성

......@@ -20,7 +20,7 @@
로비에서는 모든 방 목록을 확인하고 접속할 수 있습니다. 서버에 `roomList`를 전송하면 `RoomDescription[]`가 반환됩니다. 이 메세지는 모든 방에 관한 정보를 가지고 있습니다. 각 방은 고유한 `uuid`값으로 구분됩니다.
특정한 방에 접속하기 위해서는 서버에 `joinRoom`을 보내면 됩니다. 요청이 성공하면 `RoomInfo`가 반환됩니다. `RoomInfo`에는 본인을 제외한 다른 플레이어들의 정보만이 담겨 있습니다.
특정한 방에 접속하기 위해서는 서버에 `joinRoom`을 보내면 됩니다. 요청이 성공하면 `RoomInfo`가 반환됩니다. `RoomInfo`에는 본인을 포함한 모든 다른 플레이어들의 정보가 담겨 있습니다.
### 방
......
......@@ -66,7 +66,7 @@ export class Connection {
message: ServerInboundMessage<"login">
): ServerResponse<"login"> {
this.user = new User(message.username, this);
console.log(`User ${message.username} has logged in!`);
// console.log(`User ${message.username} has logged in!`);
return { ok: true };
}
......
import ioclient, { Socket } from "socket.io-client";
import { expect } from "chai";
import { Server } from "./Server";
import { response } from "express";
import {
RawMessage,
ServerInboundMessage,
ServerInboundMessageKey,
ServerOutboundMessage,
ServerOutboundMessageKey,
ServerResponse,
} from "../common";
describe("server", () => {
const PORT = 3000;
var server: Server;
var client1: Socket;
var client2: Socket;
before((done) => {
let connected = 0;
client1 = ioclient(`http://localhost:${PORT}`);
client1.on("connect", () => ++connected == 2 && done());
client2 = ioclient(`http://localhost:${PORT}`);
client2.on("connect", () => ++connected == 2 && done());
server = new Server(3000);
});
after(() => {
server.close();
client1.close();
client2.close();
});
var roomUserUpdateMessage: ServerOutboundMessage<"updateRoomUser">;
var roomChatMessage: ServerOutboundMessage<"chat">;
const send = <T extends ServerInboundMessageKey>(
socket: Socket,
type: T,
message: ServerInboundMessage<T>,
callback: (response: ServerResponse<T>) => void
) => {
socket.emit(
"msg",
{
type: type as string,
message: message,
},
callback
);
};
step("register listeners", () => {
client1.on("msg", (raw: RawMessage) => {
if (raw.type == "updateRoomUser") roomUserUpdateMessage = raw.message;
});
client1.on("msg", (raw: RawMessage) => {
if (raw.type == "chat") roomChatMessage = raw.message;
});
});
step("login 1", (done) => {
send(
client1,
"login",
{
username: "guest1",
},
(response) => {
expect(response.ok).to.eq(true);
done();
}
);
});
step("login 2", (done) => {
send(
client2,
"login",
{
username: "guest2",
},
(response) => {
expect(response.ok).to.eq(true);
done();
}
);
});
var roomToJoin: string;
step("room list", (done) => {
send(client1, "roomList", {}, (response) => {
expect(response.ok).to.eq(true);
expect(response.result !== undefined).to.eq(true);
if (response.result) {
expect(response.result[0].name).to.eq("테스트 방 #1");
roomToJoin = response.result[0].uuid;
}
done();
});
});
step("room join 1", (done) => {
send(client1, "joinRoom", { uuid: roomToJoin }, (response) => {
expect(response.ok).to.eq(true);
expect(response.result !== undefined).to.eq(true);
if (response.result) {
expect(response.result.uuid).to.eq(roomToJoin);
expect(response.result.users.length).to.eq(1);
expect(response.result.users[0].username).to.eq("guest1");
}
done();
});
});
step("room join 2", (done) => {
send(client2, "joinRoom", { uuid: roomToJoin }, (response) => {
expect(response.ok).to.eq(true);
expect(response.result !== undefined).to.eq(true);
if (response.result) {
expect(response.result.uuid).to.eq(roomToJoin);
expect(response.result.users.length).to.eq(2);
}
done();
});
});
// TODO: RoomUserUpdateMessage가 아직 도착하지 않았는데 실행되는 경우
step("client 1 received user update", () => {
expect(roomUserUpdateMessage !== undefined).to.eq(true);
if (roomUserUpdateMessage) {
expect(roomUserUpdateMessage.state).to.eq("added");
expect(roomUserUpdateMessage.user.username).to.eq("guest2");
}
});
step("client 2 send chat", (done) => {
send(client2, "chat", { message: "Hello World" }, (response) => {
expect(response.ok).to.eq(true);
done();
});
});
step("client 1 received chat", () => {
expect(roomChatMessage !== undefined).to.eq(true);
if (roomChatMessage) {
expect(roomChatMessage.sender).to.eq("guest2");
expect(roomChatMessage.message).to.eq("Hello World");
}
});
step("client 2 leave", (done) => {
send(client2, "leaveRoom", {}, (response) => {
expect(response.ok).to.eq(true);
done();
});
});
step("client 1 received user update", () => {
expect(roomUserUpdateMessage !== undefined).to.eq(true);
if (roomUserUpdateMessage) {
expect(roomUserUpdateMessage.state).to.eq("removed");
expect(roomUserUpdateMessage.user.username).to.eq("guest2");
}
});
});
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { DummySocket } from "./util/DummySocket";
import { SocketTester } from "./util/SocketTester";
describe("채팅", () => {
const roomManager = new RoomManager();
it("로그인하지 않은 유저는 요청할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
const response = socket.test("chat", { message: "Hello World" });
expect(response.ok).eq(false);
});
it("방에 접속하지 않은 유저는 요청할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const response = socket.test("chat", { message: "Hello World" });
expect(response.ok).eq(false);
});
it("채팅을 보냅니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room = roomManager.create("테스트", 2);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket.test("chat", { message: "Hello World" }).ok).eq(true);
});
it("자신을 제외한 다른 사람들이 채팅을 받습니다", () => {
const socket1 = new SocketTester(roomManager);
socket1.login("guest1");
const socket2 = new SocketTester(roomManager);
socket2.login("guest2");
const room = roomManager.create("테스트", 2);
expect(socket1.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket1.test("chat", { message: "Hello World" }).ok).eq(true);
expect(socket2.socket.received("chat").message).eq("Hello World");
expect(socket2.socket.received("chat").sender).eq("guest1");
socket1.socket.notReceived("chat");
});
it("빈 채팅은 보낼 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room = roomManager.create("테스트", 2);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket.test("chat", { message: "" }).ok).eq(false);
expect(socket.test("chat", { message: " " }).ok).eq(false);
});
it("글자수가 300자를 넘는 채팅은 보낼 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room = roomManager.create("테스트", 2);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket.test("chat", { message: "A".repeat(300) }).ok).eq(true);
expect(socket.test("chat", { message: "A".repeat(301) }).ok).eq(false);
expect(socket.test("chat", { message: "가".repeat(300) }).ok).eq(true);
expect(socket.test("chat", { message: "가".repeat(301) }).ok).eq(false);
});
});
import { expect } from "chai";
import { Connection } from "../connection/Connection";
import { SocketWrapper } from "../connection/SocketWrapper";
import { RoomManager } from "../room/RoomManager";
import { DummySocket } from "./util/DummySocket";
import { SocketTester } from "./util/SocketTester";
describe("유효하지 않은 메세지", () => {
const roomManager = new RoomManager();
it("빈 메세지는 실패합니다", () => {
const socket = new SocketTester();
const socket = new SocketTester(roomManager);
const response = socket.testAny({});
expect(response.ok).eq(false);
});
it("json이 아닌 메세지는 실패합니다", () => {
const socket = new SocketTester(roomManager);
expect(socket.testAny("lol").ok).eq(false);
expect(socket.testAny(3.14).ok).eq(false);
expect(socket.testAny(false).ok).eq(false);
expect(socket.testAny(null).ok).eq(false);
expect(socket.testAny(undefined).ok).eq(false);
});
it("유효하지 않은 타입의 메세지는 실패합니다", () => {
const socket = new SocketTester();
const socket = new SocketTester(roomManager);
const response = socket.testRaw({ type: "wtf", message: {} });
expect(response.ok).eq(false);
});
it("유효한 타입이지만 내용이 유효하지 않은 메세지는 실패합니다", () => {
const socket = new SocketTester();
const response = socket.testRaw({ type: "login", message: {} });
it("유효한 타입이지만 내용이 빈 메세지는 실패합니다", () => {
const socket = new SocketTester(roomManager);
expect(socket.testRaw({ type: "login", message: {} }).ok).eq(false);
expect(socket.testRaw({ type: "chat", message: {} }).ok).eq(false);
});
it("유효한 타입이지만 속성의 타입이 매칭되지 않는 메세지는 실패합니다", () => {
const socket = new SocketTester(roomManager);
expect(
socket.testRaw({
type: "login",
message: { username: 1234 },
}).ok
).eq(false);
expect(
socket.testRaw({
type: "login",
message: { username: {} },
}).ok
).eq(false);
expect(
socket.testRaw({
type: "login",
message: { username: [false, true, false] },
}).ok
).eq(false);
});
it("유효한 타입이지만 불필요한 속성이 포함된 메세지는 실패합니다", () => {
const socket = new SocketTester(roomManager);
const response = socket.testRaw({
type: "login",
message: { username: "guest", hello: "world" },
});
expect(response.ok).eq(false);
});
});
......
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { DummySocket } from "./util/DummySocket";
import { SocketTester } from "./util/SocketTester";
describe("방 입장", () => {
const roomManager = new RoomManager();
it("로그인하지 않은 유저는 요청할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
const room = roomManager.create("테스트", 2);
const response = socket.test("joinRoom", { uuid: room.uuid });
expect(response.ok).eq(false);
});
it("방에 입장합니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest1");
const room = roomManager.create("테스트", 2);
const response = socket.test("joinRoom", { uuid: room.uuid });
expect(response.ok).eq(true);
expect(response.result?.uuid).eq(room.uuid);
expect(response.result?.name).eq(room.name);
expect(response.result?.maxUsers).eq(room.maxUsers);
expect(response.result?.users?.length).eq(1);
expect(response.result?.users[0]?.username).eq("guest1");
});
it("방에 입장하면 유저 목록이 업데이트 됩니다", () => {
const socket1 = new SocketTester(roomManager);
const socket2 = new SocketTester(roomManager);
socket1.login("guest1");
socket2.login("guest2");
const room = roomManager.create("테스트", 2);
expect(socket1.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
const updated = socket1.socket.received("updateRoomUser");
expect(updated.state).eq("added");
expect(updated.user.username).eq("guest2");
});
it("방에 이미 입장한 상태에서 다른 방에 입장할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room1 = roomManager.create("테스트1", 2);
const room2 = roomManager.create("테스트2", 2);
expect(socket.test("joinRoom", { uuid: room1.uuid }).ok).eq(true);
expect(socket.test("joinRoom", { uuid: room2.uuid }).ok).eq(false);
});
it("가득 찬 방에는 입장할 수 없습니다", () => {
const socket1 = new SocketTester(roomManager);
const socket2 = new SocketTester(roomManager);
socket1.login("guest1");
socket2.login("guest2");
const room = roomManager.create("테스트", 1);
expect(socket1.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(false);
});
});
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { User } from "../user/User";
import { DummySocket } from "./util/DummySocket";
import { SocketTester } from "./util/SocketTester";
describe("방 퇴장", () => {
const roomManager = new RoomManager();
it("로그인하지 않은 유저는 요청할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
const response = socket.test("leaveRoom", {});
expect(response.ok).eq(false);
});
it("방에 입장하지 않은 유저는 요청할 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const response = socket.test("leaveRoom", {});
expect(response.ok).eq(false);
});
it("방에서 퇴장합니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room = roomManager.create("테스트", 2);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket.test("leaveRoom", {}).ok).eq(true);
});
it("방에서 퇴장한 뒤 다시 요청을 보낼 수 없습니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
const room = roomManager.create("테스트", 2);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket.test("leaveRoom", {}).ok).eq(true);
expect(socket.test("leaveRoom", {}).ok).eq(false);
});
it("방에서 퇴장하면 유저 목록이 업데이트 됩니다", () => {
const socket1 = new SocketTester(roomManager);
const socket2 = new SocketTester(roomManager);
socket1.login("guest1");
socket2.login("guest2");
const room = roomManager.create("테스트", 2);
expect(socket1.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(socket2.test("leaveRoom", {}).ok).eq(true);
const updated = socket1.socket.received("updateRoomUser");
expect(updated.state).eq("removed");
expect(updated.user.username).eq("guest2");
});
});
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { SocketTester } from "./util/SocketTester";
describe("로그인", () => {
const roomManager = new RoomManager();
it("로그인합니다", () => {
const socket = new SocketTester(roomManager);
const response = socket.test("login", { username: "guest" });
expect(response.ok).eq(true);
expect(socket.connection.user?.username).eq("guest");
});
});
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { DummySocket } from "./util/DummySocket";
import { SocketTester } from "./util/SocketTester";
describe("방 목록 요청", () => {
const roomManager = new RoomManager();
it("로그인하지 않은 유저는 요청할 수 없습니다.", () => {
const socket = new SocketTester(roomManager);
const response = socket.test("roomList", {});
expect(response.ok).eq(false);
});
it("방 목록을 가져옵니다.", () => {
const roomManager = new RoomManager();
roomManager.create("테스트1", 4);
roomManager.create("테스트2", 2);
const socket = new SocketTester(roomManager);
socket.login("guest");
const response = socket.test("roomList", {});
expect(response.ok).eq(true);
expect(response.result?.length).eq(2);
expect(response.result[0].name).eq("테스트1");
expect(response.result[1].name).eq("테스트2");
});
});
import { RawMessage, ServerResponse } from "../../../common";
import { expect } from "chai";
import {
RawMessage,
ServerOutboundMessage,
ServerOutboundMessageKey,
ServerResponse,
} from "../../../common";
import { SocketWrapper } from "../../connection/SocketWrapper";
export class DummySocket implements SocketWrapper {
public handler?: (raw: RawMessage) => ServerResponse<any>;
public receivedMessages: RawMessage[] = [];
public setHandler(handler: (raw: RawMessage) => ServerResponse<any>) {
this.handler = handler;
}
public send(raw: RawMessage): void {}
public send(raw: RawMessage): void {
this.receivedMessages.push(raw);
}
private findMessageIndex<T extends ServerOutboundMessageKey>(key: T): number {
for (let i = 0; i < this.receivedMessages.length; i++) {
if (this.receivedMessages[i].type == key) {
return i;
}
}
return -1;
}
public received<T extends ServerOutboundMessageKey>(
key: T
): ServerOutboundMessage<T> {
const index = this.findMessageIndex(key);
expect(index !== -1).eq(true);
const message = this.receivedMessages[index];
this.receivedMessages.splice(index, 1);
return message.message;
}
public notReceived<T extends ServerOutboundMessageKey>(key: T): void {
expect(this.findMessageIndex(key) === -1).eq(true);
}
}
......
......@@ -10,11 +10,10 @@ import { DummySocket } from "./DummySocket";
export class SocketTester {
public readonly connection: Connection;
constructor(
public socket: DummySocket = new DummySocket(),
roomManager: RoomManager = new RoomManager()
) {
this.connection = new Connection(socket, roomManager);
public readonly socket: DummySocket;
constructor(roomManager: RoomManager) {
this.socket = new DummySocket();
this.connection = new Connection(this.socket, roomManager);
}
public test<T extends ServerInboundMessageKey>(
......@@ -34,4 +33,8 @@ export class SocketTester {
public testAny(obj: any): ServerResponse<any> {
return this.connection.handleRaw(obj);
}
public login(username: string): void {
this.test("login", { username });
}
}
......