- Add Transport class for unified communication layer - Implement new communicator patterns - Add VERSIONING.md for version management - Add crosstest files for Dart/Go integration testing - Update protocol documentation - Add new skills and templates for AI-assisted development - Various bug fixes and improvements to Dart implementation
149 lines
4 KiB
Dart
149 lines
4 KiB
Dart
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 {}
|
||
}
|
||
|
||
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로 완료ë<C592>œë‹¤', () 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(
|
||
'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)),
|
||
);
|
||
});
|
||
});
|
||
}
|