proto-socket/dart/test/communicator_test.dart

178 lines
4.9 KiB
Dart

import 'dart:async';
import 'dart:mirrors';
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<void> send<T extends GeneratedMessage>(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 = <PacketBase>[];
Object? error;
@override
Future<void> writePacket(PacketBase base) async {
final error = this.error;
if (error != null) {
throw error;
}
sentPackets.add(base);
}
@override
Future<void> close() async {}
}
Map<int, Object?> _pendingRequestsOf(Communicator communicator) {
final library = reflectClass(Communicator).owner as LibraryMirror;
final symbol = MirrorSystem.getSymbol('_pendingRequests', library);
return reflect(communicator).getField(symbol).reflectee as Map<int, Object?>;
}
void main() {
group('Communicator protocol guards', () {
test('response typeName mismatch completes sendRequest with error',
() async {
final communicator = _FakeCommunicator();
final future = communicator.sendRequest<TestData, TestData>(
TestData()
..index = 1
..message = 'hello',
);
await Future<void>.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<StateError>().having((error) => error.toString(), 'message',
contains('Response type mismatch')),
),
);
});
test('close 후 sendRequest는 StateError로 완료된다', () async {
final communicator = _FakeCommunicator();
final future = communicator.sendRequest<TestData, TestData>(
TestData()..index = 1,
);
await Future<void>.delayed(Duration.zero);
communicator.closeForTest();
await expectLater(
future,
throwsA(
isA<StateError>().having(
(error) => error.message,
'message',
contains('connection closed'),
),
),
);
});
test('sendRequest가 timeout 내 응답 없으면 TimeoutException을 던진다', () async {
final communicator = _FakeCommunicator();
final future = communicator.sendRequest<TestData, TestData>(
TestData()
..index = 1
..message = 'wait',
timeout: const Duration(milliseconds: 50),
);
await Future<void>.delayed(Duration.zero);
expect(communicator.transport.sentPackets, hasLength(1));
await expectLater(
future,
throwsA(isA<TimeoutException>()),
);
expect(_pendingRequestsOf(communicator), isEmpty);
});
test(
'cannot register addRequestListener for a type already using addListener',
() {
final communicator = _FakeCommunicator();
communicator.addListener<TestData>((_) {});
expect(
() => communicator.addRequestListener<TestData, TestData>(
(req) async => TestData()..index = req.index,
),
throwsA(isA<StateError>()),
);
});
test(
'cannot register addListener for a type already using addRequestListener',
() {
final communicator = _FakeCommunicator();
communicator.addRequestListener<TestData, TestData>(
(req) async => TestData()..index = req.index,
);
expect(
() => communicator.addListener<TestData>((_) {}),
throwsA(isA<StateError>()),
);
});
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)),
);
});
});
}