import 'package:protobuf/protobuf.dart'; import 'package:test/test.dart'; import 'package:toki_socket/toki_socket.dart'; class _FakeCommunicator extends Communicator { final _FakeTransport transport = _FakeTransport(); _FakeCommunicator() { isAlive = true; initialize({ TestData.getDefault().info_.qualifiedMessageName: TestData.fromBuffer, HeartBeat.getDefault().info_.qualifiedMessageName: HeartBeat.fromBuffer, }, transport: transport); } void closeForTest() { isAlive = false; cancelPendingRequests(); } @override Future send(T data) async { if (isAlive) { await queuePacket(PacketBase() ..typeName = data.info_.qualifiedMessageName ..nonce = ++nonce ..data = data.writeToBuffer()); } return Future.value(); } } class _FakeTransport implements Transport { final sentPackets = []; Object? error; @override Future writePacket(PacketBase base) async { final error = this.error; if (error != null) { throw error; } sentPackets.add(base); } @override Future close() async {} } void main() { group('Communicator protocol guards', () { test('response typeName mismatch completes sendRequest with error', () async { final communicator = _FakeCommunicator(); final future = communicator.sendRequest( TestData() ..index = 1 ..message = 'hello', ); await Future.delayed(Duration.zero); final requestNonce = communicator.transport.sentPackets.single.nonce; communicator.onReceivedData( HeartBeat.getDefault().info_.qualifiedMessageName, HeartBeat().writeToBuffer(), responseNonce: requestNonce, ); await expectLater( future, throwsA( isA().having((error) => error.toString(), 'message', contains('Response type mismatch')), ), ); }); test('close 후 sendRequest는 StateError로 완료된다', () async { final communicator = _FakeCommunicator(); final future = communicator.sendRequest( TestData()..index = 1, ); await Future.delayed(Duration.zero); communicator.closeForTest(); await expectLater( future, throwsA( isA().having( (error) => error.message, 'message', contains('connection closed'), ), ), ); }); test( 'cannot register addRequestListener for a type already using addListener', () { final communicator = _FakeCommunicator(); communicator.addListener((_) {}); expect( () => communicator.addRequestListener( (req) async => TestData()..index = req.index, ), throwsA(isA()), ); }); test( 'cannot register addListener for a type already using addRequestListener', () { final communicator = _FakeCommunicator(); communicator.addRequestListener( (req) async => TestData()..index = req.index, ); expect( () => communicator.addListener((_) {}), throwsA(isA()), ); }); test('queuePacket uses injected transport', () async { final communicator = _FakeCommunicator(); final packet = PacketBase() ..typeName = TestData.getDefault().info_.qualifiedMessageName ..nonce = 1 ..data = (TestData()..index = 7).writeToBuffer(); await communicator.queuePacket(packet); expect(communicator.transport.sentPackets, hasLength(1)); expect(communicator.transport.sentPackets.single, same(packet)); final error = StateError('write failed'); communicator.transport.error = error; await expectLater( communicator.queuePacket(PacketBase()..typeName = 'TestData'), throwsA(same(error)), ); }); }); }