Files
eatvio_app/test/features/chat/presentation/bloc/chats_cubit_test.dart

1041 lines
32 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/core/models/pagination_model.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_state.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'chats_cubit_test.mocks.dart';
@GenerateNiceMocks([
MockSpec<ChatRepository>(),
MockSpec<ProfileRepository>(),
MockSpec<WsManager>(),
MockSpec<StreamSubscription>(),
])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('ChatsCubit', () {
late ChatsCubit chatsCubit;
late MockChatRepository mockChatRepository;
late MockProfileRepository mockProfileRepository;
late MockWsManager mockWsManager;
const testChatRoomsPagination = ChatRoomPaginationModel(
data: [],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 0,
),
);
setUp(() {
mockChatRepository = MockChatRepository();
mockProfileRepository = MockProfileRepository();
mockWsManager = MockWsManager();
chatsCubit = ChatsCubit(
chatRepository: mockChatRepository,
wsManager: mockWsManager,
profileRepository: mockProfileRepository,
);
});
tearDown(() async {
await Future.delayed(const Duration(milliseconds: 100));
if (!chatsCubit.isClosed) {
await chatsCubit.close();
}
});
ChatRoom buildChatRoom(String chatId, {int unread = 0}) {
return ChatRoom(
id: chatId,
lastMessage: null,
users: const Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: unread,
chosenPlan: null,
);
}
ChannelReadEvent socketEvent({
required int id,
required int authorId,
required String roomId,
String data = 'socket message',
}) {
return _FakeChannelReadEvent(
jsonEncode(<String, dynamic>{
'id': id,
'data': data,
'media_data': null,
'author_id': authorId,
'updated_at': DateTime.now().toIso8601String(),
'read_by_to_user': false,
'is_author_message_auth_user': false,
'room_id': roomId,
}),
);
}
Future<StreamController<ChannelReadEvent>> connectToSocketStream() async {
final controller = StreamController<ChannelReadEvent>.broadcast();
when(
mockWsManager.newMessagesStream,
).thenAnswer((_) => controller.stream);
await chatsCubit.connectToWebSocket();
return controller;
}
test('начальное состояние - initial', () {
expect(chatsCubit.state, isA<ChatsState>());
expect(chatsCubit.state.state, BaseState.initial);
});
test(
'getChatRoomsWithLoading - загружает комнаты с loading состоянием',
() async {
when(
mockChatRepository.getChatRooms(query: null),
).thenAnswer((_) async => testChatRoomsPagination);
await chatsCubit.getChatRoomsWithLoading();
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.success);
verify(mockChatRepository.getChatRooms(query: null)).called(1);
},
);
test('getChatRoomsWithLoading - обрабатывает ошибку', () async {
when(
mockChatRepository.getChatRooms(query: null),
).thenThrow(Exception('Network error'));
await chatsCubit.getChatRoomsWithLoading();
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.error);
expect(chatsCubit.state.errorMessage, isNotNull);
});
test('getChatRooms - загружает комнаты без loading состояния', () async {
when(
mockChatRepository.getChatRooms(query: null),
).thenAnswer((_) async => testChatRoomsPagination);
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.success);
verify(mockChatRepository.getChatRooms(query: null)).called(1);
});
test('getChatRooms - загружает комнаты с запросом', () async {
const query = 'test query';
when(
mockChatRepository.getChatRooms(query: query),
).thenAnswer((_) async => testChatRoomsPagination);
await chatsCubit.getChatRooms(query: query);
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.success);
verify(mockChatRepository.getChatRooms(query: query)).called(1);
});
test('loadMoreChatRooms - загружает больше комнат', () async {
// Create pagination with lastPage > 1 so loadMoreChatRooms doesn't return early
const paginationPage1 = ChatRoomPaginationModel(
data: [],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 3, // Set to 3 so we can load more
links: [],
perPage: 10,
to: null,
total: 30,
),
);
const paginationPage2 = ChatRoomPaginationModel(
data: [],
unreadChatsCount: 0,
meta: Meta(
currentPage: 2,
from: null,
lastPage: 3,
links: [],
perPage: 10,
to: null,
total: 30,
),
);
when(
mockChatRepository.getChatRooms(query: null, page: 1),
).thenAnswer((_) async => paginationPage1);
// Сначала загружаем первую страницу
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
// Затем загружаем следующую страницу
when(
mockChatRepository.getChatRooms(query: null, page: 2),
).thenAnswer((_) async => paginationPage2);
await chatsCubit.loadMoreChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
verify(mockChatRepository.getChatRooms(query: null, page: 2)).called(1);
});
test('loadMoreChatRooms - не загружает если уже загружается', () async {
when(
mockChatRepository.getChatRooms(query: null),
).thenAnswer((_) async => testChatRoomsPagination);
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
// Устанавливаем isLoadingMoreChats в true
chatsCubit.emit(chatsCubit.state.copyWith(isLoadingMoreChats: true));
await chatsCubit.loadMoreChatRooms();
// Не должно вызывать репозиторий
verifyNever(mockChatRepository.getChatRooms(query: null, page: 2));
});
test(
'reduceUnreadMessagesCount - уменьшает количество непрочитанных сообщений',
() {
const chatId = 'chat-id';
const count = 2;
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 5,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 1,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: pagination.data,
chatsWithUnreadMessagesCount: 1,
),
);
chatsCubit.reduceUnreadMessagesCount(chatId: chatId, count: count);
final updatedState = chatsCubit.state;
final updatedRoom = updatedState.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(updatedRoom.unreadMessagesCount, 3);
},
);
test('connectToWebSocket - подключается к WebSocket', () async {
const mockStream = Stream<ChannelReadEvent>.empty();
when(mockWsManager.newMessagesStream).thenAnswer((_) => mockStream);
await chatsCubit.connectToWebSocket();
verify(mockWsManager.newMessagesStream).called(1);
});
test(
'_handleNewMessageViaSocket - собственное сообщение обновляет lastMessage без непрочитанного',
() async {
const chatId = 'chat-id';
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(socketEvent(id: 100, authorId: 42, roomId: chatId));
await Future.delayed(const Duration(milliseconds: 100));
final room = chatsCubit.state.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(room.lastMessage?.id, 100);
expect(room.unreadMessagesCount, 0);
expect(chatsCubit.state.chatsWithUnreadMessagesCount, 0);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - чужое сообщение увеличивает непрочитанное',
() async {
const chatId = 'chat-id';
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(socketEvent(id: 200, authorId: 7, roomId: chatId));
await Future.delayed(const Duration(milliseconds: 100));
final room = chatsCubit.state.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(room.lastMessage?.id, 200);
expect(room.unreadMessagesCount, 1);
expect(chatsCubit.state.chatsWithUnreadMessagesCount, 1);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - повторная доставка того же id не двоит непрочитанное',
() async {
const chatId = 'chat-id';
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(socketEvent(id: 300, authorId: 7, roomId: chatId));
await Future.delayed(const Duration(milliseconds: 100));
controller.add(socketEvent(id: 300, authorId: 7, roomId: chatId));
await Future.delayed(const Duration(milliseconds: 100));
final room = chatsCubit.state.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(room.unreadMessagesCount, 1);
expect(chatsCubit.state.chatsWithUnreadMessagesCount, 1);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - чужое сообщение в непрочитанном чате не поднимает счётчик чатов',
() async {
const chatId = 'chat-id';
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId, unread: 2)],
chatsWithUnreadMessagesCount: 1,
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(socketEvent(id: 400, authorId: 7, roomId: chatId));
await Future.delayed(const Duration(milliseconds: 100));
final room = chatsCubit.state.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(room.unreadMessagesCount, 3);
expect(chatsCubit.state.chatsWithUnreadMessagesCount, 1);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - чужой старт видеозвонка показывает входящий вызов',
() async {
const chatId = 'chat-id';
final presented = <String>[];
chatsCubit.presentIncomingVideoCall = (payload) async {
presented.add(payload.conversationUuid);
};
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(
socketEvent(
id: 500,
authorId: 7,
roomId: chatId,
data: 'Анна начал(а) видеозвонок.',
),
);
await Future.delayed(const Duration(milliseconds: 100));
expect(presented, [chatId]);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - свой старт видеозвонка не показывает входящий вызов',
() async {
const chatId = 'chat-id';
final presented = <String>[];
chatsCubit.presentIncomingVideoCall = (payload) async {
presented.add(payload.conversationUuid);
};
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(
socketEvent(
id: 501,
authorId: 42,
roomId: chatId,
data: 'Анна начал(а) видеозвонок.',
),
);
await Future.delayed(const Duration(milliseconds: 100));
expect(presented, isEmpty);
await controller.close();
},
);
test(
'_handleNewMessageViaSocket - завершение видеозвонка закрывает только этот чат',
() async {
const chatId = 'chat-id';
final dismissed = <String?>[];
chatsCubit.dismissIncomingVideoCall = (uuid) async {
dismissed.add(uuid);
};
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: [buildChatRoom(chatId)],
),
);
when(mockWsManager.userId).thenReturn(42);
final controller = await connectToSocketStream();
controller.add(
socketEvent(
id: 502,
authorId: 7,
roomId: chatId,
data: 'Анна завершил(а) видеозвонок.',
),
);
await Future.delayed(const Duration(milliseconds: 100));
expect(dismissed, [chatId]);
await controller.close();
},
);
test('close - закрывает cubit и отписывается от подписок', () async {
final mockSubscription1 = MockStreamSubscription();
when(mockSubscription1.cancel()).thenAnswer((_) async => Future.value());
// Set newIncomingMessagesSubscription
chatsCubit.newIncomingMessagesSubscription = mockSubscription1;
await chatsCubit.close();
// Verify that cancel was called on newIncomingMessagesSubscription
verify(mockSubscription1.cancel()).called(1);
// newLastMessageSubscription is cancelled in super.close() which we can't easily verify
// but the test verifies that the method completes without error
});
test(
'reduceUnreadMessagesCount - уменьшает до 0 и уменьшает chatsWithUnreadMessagesCount',
() {
const chatId = 'chat-id';
const count = 5;
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 5,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 1,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: pagination.data,
chatsWithUnreadMessagesCount: 1,
),
);
chatsCubit.reduceUnreadMessagesCount(chatId: chatId, count: count);
final updatedState = chatsCubit.state;
final updatedRoom = updatedState.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(updatedRoom.unreadMessagesCount, 0);
expect(updatedState.chatsWithUnreadMessagesCount, 0);
},
);
test(
'reduceUnreadMessagesCount - не уменьшает chatsWithUnreadMessagesCount если unreadMessagesCount > 0',
() {
const chatId = 'chat-id';
const count = 2;
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 5,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 1,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: pagination.data,
chatsWithUnreadMessagesCount: 1,
),
);
chatsCubit.reduceUnreadMessagesCount(chatId: chatId, count: count);
final updatedState = chatsCubit.state;
expect(updatedState.chatsWithUnreadMessagesCount, 1);
},
);
test('loadMoreChatRooms - не загружает если lastPage == page', () async {
const paginationPage1 = ChatRoomPaginationModel(
data: [],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 10,
),
);
when(
mockChatRepository.getChatRooms(query: null, page: 1),
).thenAnswer((_) async => paginationPage1);
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
// lastPage = meta.lastPage + 1 = 2, page = 2, поэтому не должно загружать
await chatsCubit.loadMoreChatRooms();
verifyNever(mockChatRepository.getChatRooms(query: null, page: 2));
});
test('loadMoreChatRooms - обрабатывает ошибку', () async {
const paginationPage1 = ChatRoomPaginationModel(
data: [],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 3,
links: [],
perPage: 10,
to: null,
total: 30,
),
);
when(
mockChatRepository.getChatRooms(query: null, page: 1),
).thenAnswer((_) async => paginationPage1);
when(
mockChatRepository.getChatRooms(query: null, page: 2),
).thenThrow(Exception('Ошибка загрузки'));
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
await chatsCubit.loadMoreChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.error);
expect(chatsCubit.state.errorMessage, isNotNull);
expect(chatsCubit.state.isLoadingMoreChats, false);
});
test('getChatRooms - обрабатывает ошибку', () async {
when(
mockChatRepository.getChatRooms(query: null),
).thenThrow(Exception('Network error'));
await chatsCubit.getChatRooms();
await Future.delayed(const Duration(milliseconds: 100));
expect(chatsCubit.state.state, BaseState.error);
expect(chatsCubit.state.errorMessage, isNotNull);
});
test('_setNewLastMessage - обновляет последнее сообщение в чате', () async {
const chatId = 'chat-id';
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 0,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(state: BaseState.success, chatRooms: pagination.data),
);
final newMessage = ChatMessageModel(
id: 1,
data: 'Test message',
mediaData: null,
urlsMediaInfo: null,
authorId: 1,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: true,
roomId: chatId,
type: ChatMessageType.text,
);
// Используем рефлексию для вызова приватного метода через тестовый доступ
// Или проверяем через публичный API, если есть
// В данном случае, _setNewLastMessage вызывается через newUserMessagesStream
final streamController = StreamController<ChatMessageModel>();
when(
mockChatRepository.newUserMessagesStream,
).thenAnswer((_) => streamController.stream);
// Пересоздаем cubit чтобы подписаться на stream
await chatsCubit.close();
chatsCubit = ChatsCubit(
chatRepository: mockChatRepository,
wsManager: mockWsManager,
profileRepository: mockProfileRepository,
);
chatsCubit.emit(
ChatsState(state: BaseState.success, chatRooms: pagination.data),
);
streamController.add(newMessage);
await Future.delayed(const Duration(milliseconds: 100));
final updatedState = chatsCubit.state;
final updatedRoom = updatedState.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(updatedRoom.lastMessage, isNotNull);
expect(updatedRoom.lastMessage?.id, 1);
await streamController.close();
});
test(
'_setNewLastMessage - обновляет последнее сообщение через newUserMessagesStream',
() async {
const chatId = 'chat-id';
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 0,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 0,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
final newMessage = ChatMessageModel(
id: 1,
data: 'Test message',
mediaData: null,
urlsMediaInfo: null,
authorId: 1,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: true,
roomId: chatId,
type: ChatMessageType.text,
);
final streamController = StreamController<ChatMessageModel>();
when(
mockChatRepository.newUserMessagesStream,
).thenAnswer((_) => streamController.stream);
// Пересоздаем cubit чтобы подписаться на stream
await chatsCubit.close();
chatsCubit = ChatsCubit(
chatRepository: mockChatRepository,
wsManager: mockWsManager,
profileRepository: mockProfileRepository,
);
chatsCubit.emit(
ChatsState(state: BaseState.success, chatRooms: pagination.data),
);
streamController.add(newMessage);
await Future.delayed(const Duration(milliseconds: 100));
final updatedState = chatsCubit.state;
final updatedRoom = updatedState.chatRooms.firstWhere(
(r) => r.id == chatId,
);
expect(updatedRoom.lastMessage, isNotNull);
expect(updatedRoom.lastMessage?.id, 1);
await streamController.close();
},
);
test(
'reduceUnreadMessagesCount - обрабатывает чат который не существует',
() {
const chatId = 'non-existent-chat';
const count = 2;
const chatRoom = ChatRoom(
id: 'other-chat-id',
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 5,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 1,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: pagination.data,
chatsWithUnreadMessagesCount: 1,
),
);
chatsCubit.reduceUnreadMessagesCount(chatId: chatId, count: count);
final updatedState = chatsCubit.state;
// Чат не должен измениться
expect(updatedState.chatRooms.length, 1);
expect(updatedState.chatRooms.first.unreadMessagesCount, 5);
expect(updatedState.chatsWithUnreadMessagesCount, 1);
},
);
test(
'reduceUnreadMessagesCount - обрабатывает уменьшение больше чем unreadMessagesCount',
() {
const chatId = 'chat-id';
const count = 10; // Больше чем unreadMessagesCount
const chatRoom = ChatRoom(
id: chatId,
lastMessage: null,
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'User 1',
profilePhotoPath: null,
profilePhotoUrl: '',
),
toChatUser: ChatUser(
id: 2,
name: 'User 2',
profilePhotoPath: null,
profilePhotoUrl: '',
),
),
unreadMessagesCount: 5,
chosenPlan: null,
);
const pagination = ChatRoomPaginationModel(
data: [chatRoom],
unreadChatsCount: 1,
meta: Meta(
currentPage: 1,
from: null,
lastPage: 1,
links: [],
perPage: 10,
to: null,
total: 1,
),
);
chatsCubit.emit(
ChatsState(
state: BaseState.success,
chatRooms: pagination.data,
chatsWithUnreadMessagesCount: 1,
),
);
chatsCubit.reduceUnreadMessagesCount(chatId: chatId, count: count);
final updatedState = chatsCubit.state;
final updatedRoom = updatedState.chatRooms.firstWhere(
(r) => r.id == chatId,
);
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();
},
);
});
}
/// Облегчённый фейк события канала: ChatsCubit использует только event.data,
/// поэтому реальный ChannelReadEvent (требует Channel) не нужен.
class _FakeChannelReadEvent extends Fake implements ChannelReadEvent {
_FakeChannelReadEvent(this.data);
@override
final dynamic data;
}