import 'dart:async'; import 'dart:io'; import 'package:test/test.dart'; import 'package:toki_socket/toki_socket.dart'; const _testPort = 19090; const _testPortSsl = 19091; const _testPortWs = 19092; const _testPortWss = 19093; const _host = 'localhost'; const _certPath = 'test/certs/server.crt'; const _keyPath = 'test/certs/server.key'; // ── 테스트 전용 픽스처 ────────────────────────────────────────── class _TestClient extends ProtobufClient { _TestClient(Socket socket) : super(socket, 5, 3, { TestData.getDefault().info_.qualifiedMessageName: TestData.fromBuffer, }); } class _TestServer extends ProtobufServer { final receivedMessages = []; final connectedClients = []; _TestServer() : super(_host, _testPort, (socket) => _TestClient(socket)); @override void onClientConnected(ProtobufClient client) { connectedClients.add(client); client.addListener((data) => receivedMessages.add(data)); } } class _TestServerSsl extends ProtobufServer { final receivedMessages = []; final connectedClients = []; _TestServerSsl(SecurityContext ctx) : super.secure(_host, _testPortSsl, ctx, (socket) => _TestClient(socket)); @override void onClientConnected(ProtobufClient client) { connectedClients.add(client); client.addListener((data) => receivedMessages.add(data)); } } class _TestWsClient extends WsProtobufClient { _TestWsClient(WebSocket ws) : super(ws, 5, 3, { TestData.getDefault().info_.qualifiedMessageName: TestData.fromBuffer, }); } class _TestWsServer extends WsProtobufServer { final receivedMessages = []; final connectedClients = []; _TestWsServer() : super(_host, _testPortWs, (ws) => _TestWsClient(ws)); @override void onClientConnected(WsProtobufClient client) { connectedClients.add(client); client.addListener((data) => receivedMessages.add(data)); } } class _TestWsServerSsl extends WsProtobufServer { final receivedMessages = []; final connectedClients = []; _TestWsServerSsl(SecurityContext ctx) : super.secure(_host, _testPortWss, ctx, (ws) => _TestWsClient(ws)); @override void onClientConnected(WsProtobufClient client) { connectedClients.add(client); client.addListener((data) => receivedMessages.add(data)); } } // ── 테스트 ────────────────────────────────────────────────────── void main() { // ── Plain TCP ──────────────────────────────────────────────── group('ProtobufServer (plain)', () { late _TestServer server; setUp(() async { server = _TestServer(); await server.start(); }); tearDown(() async { await server.stop(); }); test('서버가 정상 시작된다', () { expect(server.started, isTrue); expect(server.isSecure, isFalse); }); }); group('ProtobufClient (plain)', () { late _TestServer server; late _TestClient client; setUp(() async { server = _TestServer(); await server.start(); final socket = await ProtobufClient.connect(_host, _testPort); client = _TestClient(socket); await Future.delayed(const Duration(milliseconds: 100)); }); tearDown(() async { client.dispose(); await Future.delayed(const Duration(milliseconds: 100)); await server.stop(); }); test('클라이언트가 서버에 연결된다', () { expect(server.connectedClients, isNotEmpty); }); test('TestData 메시지를 서버가 수신한다', () async { await client.send(TestData() ..index = 42 ..message = 'hello toki-socket'); await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(1)); expect(server.receivedMessages.first.index, equals(42)); expect(server.receivedMessages.first.message, equals('hello toki-socket')); }); test('여러 메시지를 순서대로 수신한다', () async { for (var i = 0; i < 5; i++) { await client.send(TestData() ..index = i ..message = 'msg$i'); } await Future.delayed(const Duration(milliseconds: 300)); expect(server.receivedMessages, hasLength(5)); for (var i = 0; i < 5; i++) { expect(server.receivedMessages[i].index, equals(i)); } }); test('서버에서 클라이언트로 메시지를 전송한다', () async { final completer = Completer(); client.addListener((data) => completer.complete(data)); await Future.delayed(const Duration(milliseconds: 100)); await server.connectedClients.first.send(TestData() ..index = 99 ..message = 'from server'); final received = await completer.future.timeout(const Duration(seconds: 2)); expect(received.index, equals(99)); expect(received.message, equals('from server')); }); test('nonce가 송신마다 증가한다', () async { for (var i = 1; i <= 3; i++) { await client.send(TestData()..index = i); } await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(3)); }); test('클라이언트 disconnect 시 서버 콜백이 호출된다', () async { final completer = Completer(); server.connectedClients.first.addDisconnectListener((_) { if (!completer.isCompleted) completer.complete(); }); client.dispose(); await completer.future.timeout(const Duration(seconds: 2)); expect(completer.isCompleted, isTrue); }); test('HeartBeat interval 동안 연결이 유지된다', () async { await Future.delayed(const Duration(seconds: 2)); await client.send(TestData() ..index = 1 ..message = 'alive check'); await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(1)); }); }); // ── SSL/TLS ────────────────────────────────────────────────── group('ProtobufServer (SSL)', () { late _TestServerSsl server; setUp(() async { final ctx = SecurityContext() ..useCertificateChain(_certPath) ..usePrivateKey(_keyPath); server = _TestServerSsl(ctx); await server.start(); }); tearDown(() async { await server.stop(); }); test('SSL 서버가 정상 시작된다', () { expect(server.started, isTrue); expect(server.isSecure, isTrue); }); }); group('ProtobufClient (SSL)', () { late _TestServerSsl server; late _TestClient client; setUp(() async { final serverCtx = SecurityContext() ..useCertificateChain(_certPath) ..usePrivateKey(_keyPath); server = _TestServerSsl(serverCtx); await server.start(); final clientCtx = SecurityContext() ..setTrustedCertificates(_certPath); final socket = await ProtobufClient.connectSecure( _host, _testPortSsl, context: clientCtx, ); client = _TestClient(socket); await Future.delayed(const Duration(milliseconds: 100)); }); tearDown(() async { client.dispose(); await Future.delayed(const Duration(milliseconds: 100)); await server.stop(); }); test('SSL 클라이언트가 서버에 연결된다', () { expect(server.connectedClients, isNotEmpty); }); test('SSL TestData 메시지를 서버가 수신한다', () async { await client.send(TestData() ..index = 7 ..message = 'hello over ssl'); await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(1)); expect(server.receivedMessages.first.index, equals(7)); expect(server.receivedMessages.first.message, equals('hello over ssl')); }); test('SSL 서버에서 클라이언트로 메시지를 전송한다', () async { final completer = Completer(); client.addListener((data) => completer.complete(data)); await Future.delayed(const Duration(milliseconds: 100)); await server.connectedClients.first.send(TestData() ..index = 55 ..message = 'from ssl server'); final received = await completer.future.timeout(const Duration(seconds: 2)); expect(received.index, equals(55)); expect(received.message, equals('from ssl server')); }); test('SSL disconnect 시 서버 콜백이 호출된다', () async { final completer = Completer(); server.connectedClients.first.addDisconnectListener((_) { if (!completer.isCompleted) completer.complete(); }); client.dispose(); await completer.future.timeout(const Duration(seconds: 2)); expect(completer.isCompleted, isTrue); }); }); // ── WebSocket ──────────────────────────────────────────────── group('WsProtobufServer (plain)', () { late _TestWsServer server; setUp(() async { server = _TestWsServer(); await server.start(); }); tearDown(() async { await server.stop(); }); test('WS 서버가 정상 시작된다', () { expect(server.started, isTrue); expect(server.isSecure, isFalse); }); }); group('WsProtobufClient (plain)', () { late _TestWsServer server; late _TestWsClient client; setUp(() async { server = _TestWsServer(); await server.start(); final ws = await WsProtobufClient.connect(_host, _testPortWs); client = _TestWsClient(ws); await Future.delayed(const Duration(milliseconds: 100)); }); tearDown(() async { client.dispose(); await Future.delayed(const Duration(milliseconds: 100)); await server.stop(); }); test('WS 클라이언트가 서버에 연결된다', () { expect(server.connectedClients, isNotEmpty); }); test('WS TestData 메시지를 서버가 수신한다', () async { await client.send(TestData() ..index = 42 ..message = 'hello ws'); await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(1)); expect(server.receivedMessages.first.index, equals(42)); expect(server.receivedMessages.first.message, equals('hello ws')); }); test('WS 서버에서 클라이언트로 메시지를 전송한다', () async { final completer = Completer(); client.addListener((data) => completer.complete(data)); await Future.delayed(const Duration(milliseconds: 100)); await server.connectedClients.first.send(TestData() ..index = 99 ..message = 'from ws server'); final received = await completer.future.timeout(const Duration(seconds: 2)); expect(received.index, equals(99)); expect(received.message, equals('from ws server')); }); test('WS 클라이언트 disconnect 시 서버 콜백이 호출된다', () async { final completer = Completer(); server.connectedClients.first.addDisconnectListener((_) { if (!completer.isCompleted) completer.complete(); }); client.dispose(); await completer.future.timeout(const Duration(seconds: 2)); expect(completer.isCompleted, isTrue); }); }); // ── WebSocket SSL ───────────────────────────────────────────── group('WsProtobufServer (SSL)', () { late _TestWsServerSsl server; setUp(() async { final ctx = SecurityContext() ..useCertificateChain(_certPath) ..usePrivateKey(_keyPath); server = _TestWsServerSsl(ctx); await server.start(); }); tearDown(() async { await server.stop(); }); test('WSS 서버가 정상 시작된다', () { expect(server.started, isTrue); expect(server.isSecure, isTrue); }); }); group('WsProtobufClient (SSL)', () { late _TestWsServerSsl server; late _TestWsClient client; setUp(() async { final serverCtx = SecurityContext() ..useCertificateChain(_certPath) ..usePrivateKey(_keyPath); server = _TestWsServerSsl(serverCtx); await server.start(); final clientCtx = SecurityContext()..setTrustedCertificates(_certPath); final ws = await WsProtobufClient.connectSecure( _host, _testPortWss, context: clientCtx, ); client = _TestWsClient(ws); await Future.delayed(const Duration(milliseconds: 100)); }); tearDown(() async { client.dispose(); await Future.delayed(const Duration(milliseconds: 100)); await server.stop(); }); test('WSS 클라이언트가 서버에 연결된다', () { expect(server.connectedClients, isNotEmpty); }); test('WSS TestData 메시지를 서버가 수신한다', () async { await client.send(TestData() ..index = 7 ..message = 'hello over wss'); await Future.delayed(const Duration(milliseconds: 200)); expect(server.receivedMessages, hasLength(1)); expect(server.receivedMessages.first.index, equals(7)); expect(server.receivedMessages.first.message, equals('hello over wss')); }); test('WSS disconnect 시 서버 콜백이 호출된다', () async { final completer = Completer(); server.connectedClients.first.addDisconnectListener((_) { if (!completer.isCompleted) completer.complete(); }); client.dispose(); await completer.future.timeout(const Duration(seconds: 2)); expect(completer.isCompleted, isTrue); }); }); }