import 'dart:async'; 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_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(), MockSpec(), MockSpec(), MockSpec()]) 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(); } }); test('начальное состояние - initial', () { expect(chatsCubit.state, isA()); 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.empty(); when(mockWsManager.newMessagesStream).thenAnswer((_) => mockStream); await chatsCubit.connectToWebSocket(); verify(mockWsManager.newMessagesStream).called(1); }); 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 }); }); }