fix(chat): ws manager

This commit is contained in:
Anatoliy
2026-09-16 00:10:23 +03:00
parent 8ec6d5b799
commit 27e98bdb44
6 changed files with 201 additions and 5 deletions

View File

@@ -252,5 +252,36 @@ void main() {
expect(manager.client, isNull);
});
test('reconnected - эмитит сигнал после восстановления соединения', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService, errorReporter: const NoOpErrorReporter());
final received = <void>[];
final subscription = manager.reconnected.listen(received.add);
manager.handleConnectionEstablished();
await Future<void>.delayed(Duration.zero);
expect(received, hasLength(1));
await subscription.cancel();
});
test('reconnected - переживает clear (поток не закрывается при логауте)', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService, errorReporter: const NoOpErrorReporter());
final received = <void>[];
final subscription = manager.reconnected.listen(received.add);
await manager.clear();
manager.handleConnectionEstablished();
await Future<void>.delayed(Duration.zero);
expect(received, hasLength(1));
await subscription.cancel();
});
});
}

View File

@@ -1729,5 +1729,78 @@ void main() {
expect(state.chatMessages.any((message) => message.id == 88), isTrue);
expect(state.chatMessages.firstWhere((message) => message.id == 1).isRead, isTrue);
});
test('refreshCurrentChat - подтягивает сообщение, добавленное в БД в обход сокета', () async {
const uuid = 'room-uuid';
final reconnectController = StreamController<void>.broadcast();
when(mockWsManager.reconnected).thenAnswer((_) => reconnectController.stream);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
when(mockWsManager.client).thenReturn(null);
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
when(mockWsManager.directChatStream).thenReturn(null);
when(mockWsManager.clientTypingStream).thenReturn(null);
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 100));
final missedMessage = ChatMessageModel(
id: 99,
data: 'Missed during outage',
mediaData: null,
authorId: 2,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: false,
roomId: 'chat-id',
);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer(
(_) async => ChatMessagesModel(
messagesList: [missedMessage, ...testChatMessages.messagesList],
meta: const ChatMetaModel(lastPage: 3),
),
);
reconnectController.add(null);
await Future.delayed(const Duration(milliseconds: 100));
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.chatMessages.any((message) => message.id == 99), isTrue);
expect(state.chatMessages.any((message) => message.id == 1), isTrue);
expect(chatRoomCubit.lastPage, 3);
await reconnectController.close();
});
test('refreshCurrentChat - идемпотентен: повторный реконнект не двоит сообщения', () async {
const uuid = 'room-uuid';
final reconnectController = StreamController<void>.broadcast();
when(mockWsManager.reconnected).thenAnswer((_) => reconnectController.stream);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
when(mockWsManager.client).thenReturn(null);
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
when(mockWsManager.directChatStream).thenReturn(null);
when(mockWsManager.clientTypingStream).thenReturn(null);
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 100));
reconnectController.add(null);
reconnectController.add(null);
await Future.delayed(const Duration(milliseconds: 100));
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.chatMessages.where((message) => message.id == 1).length, 1);
await reconnectController.close();
});
});
}

View File

@@ -578,6 +578,23 @@ void main() {
expect(updatedRoom.unreadMessagesCount, 0);
expect(updatedState.chatsWithUnreadMessagesCount, 0);
});
test('connectToWebSocket - обновляет список чатов после реконнекта', () async {
final reconnectController = StreamController<void>.broadcast();
when(mockWsManager.reconnected).thenAnswer((_) => reconnectController.stream);
when(mockWsManager.newMessagesStream).thenReturn(null);
when(mockChatRepository.getChatRooms(query: null)).thenAnswer((_) async => testChatRoomsPagination);
await chatsCubit.connectToWebSocket();
reconnectController.add(null);
await Future.delayed(const Duration(milliseconds: 100));
verify(mockChatRepository.getChatRooms(query: null)).called(1);
await reconnectController.close();
});
});
}