proto-socket/typescript/test/tcp.test.ts
toki 5a2a21e75a 기능: TypeScript 패키지를 devDependencies로 통합하고 WebSocket 기능을 개선했다
- @bufbuild/protobuf를 dependencies에서 devDependencies로 이동
- ws를 peerDependencies에서 devDependencies로 이동
- node_ws_server.ts에 TLS 및 라우팅 기능 추가
- node_ws_client.ts에 TCP 클라이언트 통합 기능 추가
- message_common_pb.ts 프로토콜 버퍼 코드 재생성
- 크로스테스트 클라이언트 파일들 경로 업데이트
- 테스트 파일들 설정 업데이트
- 문서(PORTING_GUIDE, README) 업데이트
2026-05-20 11:22:04 +09:00

214 lines
6.3 KiB
TypeScript

import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, test } from "vitest";
import {
addListenerTyped,
addRequestListenerTyped,
parserFromSchema,
sendRequestTyped,
} from "../src/communicator.js";
import { create, TestDataSchema, type TestData } from "../src/packets/message_common_pb.js";
import { connectTcp, connectTcpTls, TcpClient } from "../src/tcp_client.js";
import { TcpServer } from "../src/tcp_server.js";
const certsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "certs");
const cert = fs.readFileSync(path.join(certsDir, "server.crt"));
const key = fs.readFileSync(path.join(certsDir, "server.key"));
function parserMap() {
return new Map([[TestDataSchema.typeName, parserFromSchema(TestDataSchema)]]);
}
async function waitForClientMessage(client: TcpClient): Promise<TestData> {
return new Promise<TestData>((resolve) => {
addListenerTyped(client.communicator, TestDataSchema, (msg) => resolve(msg));
});
}
describe("TCP", () => {
const cleanup: Array<() => Promise<void>> = [];
afterEach(async () => {
while (cleanup.length > 0) {
const fn = cleanup.pop();
if (fn) {
await fn();
}
}
});
test("connectTcp sends and receives TestData", async () => {
const server = new TcpServer("127.0.0.1", 0, (socket) => new TcpClient(socket, 0, 0, parserMap()));
cleanup.push(async () => server.stop());
await server.start();
server.onClientConnected = (client) => {
addListenerTyped(client.communicator, TestDataSchema, async (msg) => {
if (msg.index === 11 && msg.message === "hello over tcp") {
await client.communicator.send(
create(TestDataSchema, { index: 200, message: "push from tcp server" }),
);
}
});
};
const client = await connectTcp("127.0.0.1", server.port, 0, 0, parserMap());
cleanup.push(async () => client.close());
const pushed = waitForClientMessage(client);
await client.communicator.send(create(TestDataSchema, { index: 11, message: "hello over tcp" }));
await expect(pushed).resolves.toMatchObject({
index: 200,
message: "push from tcp server",
});
});
test("sendRequest/response roundtrip", async () => {
const server = new TcpServer("127.0.0.1", 0, (socket) => new TcpClient(socket, 0, 0, parserMap()));
cleanup.push(async () => server.stop());
await server.start();
server.onClientConnected = (client) => {
addRequestListenerTyped(client.communicator, TestDataSchema, (req) =>
create(TestDataSchema, {
index: req.index * 2,
message: `echo: ${req.message}`,
}),
);
};
const client = await connectTcp("127.0.0.1", server.port, 0, 0, parserMap());
cleanup.push(async () => client.close());
await expect(
sendRequestTyped(
client.communicator,
create(TestDataSchema, { index: 21, message: "request over tcp" }),
TestDataSchema,
500,
),
).resolves.toMatchObject({
index: 42,
message: "echo: request over tcp",
});
});
test("connectTcpTls sends and receives TestData", async () => {
const server = new TcpServer(
"127.0.0.1",
0,
(socket) => new TcpClient(socket, 0, 0, parserMap()),
{ cert, key },
);
cleanup.push(async () => server.stop());
await server.start();
server.onClientConnected = (client) => {
addListenerTyped(client.communicator, TestDataSchema, async (msg) => {
if (msg.index === 12 && msg.message === "hello over tls") {
await client.communicator.send(
create(TestDataSchema, { index: 201, message: "push from tls server" }),
);
}
});
};
const client = await connectTcpTls(
"127.0.0.1",
server.port,
{ ca: cert, servername: "localhost" },
0,
0,
parserMap(),
);
cleanup.push(async () => client.close());
const pushed = waitForClientMessage(client);
await client.communicator.send(create(TestDataSchema, { index: 12, message: "hello over tls" }));
await expect(pushed).resolves.toMatchObject({
index: 201,
message: "push from tls server",
});
});
test("TLS sendRequest/response roundtrip", async () => {
const server = new TcpServer(
"127.0.0.1",
0,
(socket) => new TcpClient(socket, 0, 0, parserMap()),
{ cert, key },
);
cleanup.push(async () => server.stop());
await server.start();
server.onClientConnected = (client) => {
addRequestListenerTyped(client.communicator, TestDataSchema, (req) =>
create(TestDataSchema, {
index: req.index * 2,
message: `tls echo: ${req.message}`,
}),
);
};
const client = await connectTcpTls(
"127.0.0.1",
server.port,
{ ca: cert, servername: "localhost" },
0,
0,
parserMap(),
);
cleanup.push(async () => client.close());
await expect(
sendRequestTyped(
client.communicator,
create(TestDataSchema, { index: 22, message: "request over tls" }),
TestDataSchema,
500,
),
).resolves.toMatchObject({
index: 44,
message: "tls echo: request over tls",
});
});
test("concurrent sendRequest/response roundtrip over TCP", async () => {
const server = new TcpServer("127.0.0.1", 0, (socket) => new TcpClient(socket, 0, 0, parserMap()));
cleanup.push(async () => server.stop());
await server.start();
server.onClientConnected = (client) => {
addRequestListenerTyped(client.communicator, TestDataSchema, (req) =>
create(TestDataSchema, {
index: req.index * 2,
message: `echo: ${req.message}`,
}),
);
};
const client = await connectTcp("127.0.0.1", server.port, 0, 0, parserMap());
cleanup.push(async () => client.close());
const results = await Promise.all(
Array.from({ length: 5 }, (_, i) =>
sendRequestTyped(
client.communicator,
create(TestDataSchema, { index: 30 + i, message: `request ${i}` }),
TestDataSchema,
2000,
),
),
);
results.forEach((res, i) => {
expect(res.index).toBe((30 + i) * 2);
expect(res.message).toBe(`echo: request ${i}`);
});
});
});