feat(tests): updated tests coverage to 66.5%

This commit is contained in:
misha
2026-01-14 18:47:11 +03:00
parent 497ff19cfa
commit 13ccf56df0
102 changed files with 10146 additions and 379 deletions

View File

@@ -10,6 +10,10 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
@@ -108,7 +112,6 @@ void main() {
expect(chatRoomCubit.state, isA<ChatRoomStateError>());
});
test('markMessagesAsRead - отмечает сообщения как прочитанные', () async {
const chatId = 'chat-id';
final messagesIds = [(id: 1, index: 0)];
@@ -185,5 +188,405 @@ void main() {
verify(mockWsManager.unsubscribeDirectChatChannel()).called(1);
});
test('setTimer - устанавливает таймер', () {
chatRoomCubit.setTimer();
expect(chatRoomCubit, isNotNull);
});
test('cancelTimer - отменяет таймер', () {
chatRoomCubit.setTimer();
chatRoomCubit.cancelTimer();
expect(chatRoomCubit, isNotNull);
});
test('loadNextPage - загружает следующую страницу сообщений', () async {
const uuid = 'room-uuid';
final initialMessages = ChatMessagesModel(
messagesList: [
ChatMessageModel(
id: 1,
data: 'Initial message',
mediaData: null,
authorId: 1,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: true,
roomId: 'chat-id',
),
],
meta: const ChatMetaModel(lastPage: 2),
);
final nextPageMessages = ChatMessagesModel(
messagesList: [
ChatMessageModel(
id: 2,
data: 'Next page message',
mediaData: null,
authorId: 1,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: true,
roomId: 'chat-id',
),
],
meta: const ChatMetaModel(lastPage: 2),
);
// Сначала инициализируем чат
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => initialMessages);
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);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 200));
// Теперь загружаем следующую страницу
when(mockChatRepository.getChatMessages(uuid, 2)).thenAnswer((_) async => nextPageMessages);
await chatRoomCubit.loadNextPage(uuid);
await Future.delayed(const Duration(milliseconds: 200));
verify(mockChatRepository.getChatMessages(uuid, 2)).called(1);
});
test('loadNextPage - не загружает если уже загружается', () async {
const uuid = 'room-uuid';
final initialMessages = ChatMessagesModel(
messagesList: [
ChatMessageModel(
id: 1,
data: 'Initial message',
mediaData: null,
authorId: 1,
updatedAt: DateTime.now(),
isRead: false,
isCurrentUserAuthor: true,
roomId: 'chat-id',
),
],
meta: const ChatMetaModel(lastPage: 2),
);
// Сначала инициализируем чат
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => initialMessages);
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);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
await chatRoomCubit.initChat(uuid);
await Future.delayed(const Duration(milliseconds: 200));
// Мокаем медленный ответ для второй страницы
when(mockChatRepository.getChatMessages(uuid, 2)).thenAnswer((_) async {
await Future.delayed(const Duration(milliseconds: 100));
return initialMessages;
});
// Запускаем первую загрузку
final firstLoad = chatRoomCubit.loadNextPage(uuid);
// Пытаемся загрузить еще раз пока первая загрузка не завершена
await chatRoomCubit.loadNextPage(uuid);
await firstLoad;
await Future.delayed(const Duration(milliseconds: 100));
// Проверяем, что getChatMessages для страницы 2 был вызван только один раз
verify(mockChatRepository.getChatMessages(uuid, 2)).called(1);
});
test('checkCards - проверяет наличие карт', () async {
// Сначала инициализируем чат
when(mockChatRepository.getChatRoom(any)).thenAnswer((_) async => testChatRoom);
when(mockChatRepository.getChatMessages(any, 1)).thenAnswer((_) async => testChatMessages);
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);
when(mockChatRepository.getChosenPlan(id: anyNamed('id'))).thenAnswer((_) async => throw Exception('No plan'));
await chatRoomCubit.initChat('uuid');
await Future.delayed(const Duration(milliseconds: 200));
// Мокаем getCards без создания реальных CardModel
when(mockCardsRepository.getCards()).thenAnswer((_) async => []);
await chatRoomCubit.checkCards();
await Future.delayed(const Duration(milliseconds: 100));
verify(mockCardsRepository.getCards()).called(1);
});
test('updateChosenPlanState - обновляет состояние выбранного плана', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
const testPlan = PlanModel(
id: 1,
slug: 'test-plan',
name: 'Test Plan',
image: 'https://example.com/image.jpg',
duration: 7,
state: 'active',
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
details: 'Test details',
packetsCount: 3,
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
isFavorite: false,
minPriceOfOptions: 1000,
maxPriceOfOptions: 2000,
wasLikedByMe: false,
);
final chosenPlan = ChosenPlanModel(
id: planId,
optionType: 'options1',
startDate: DateTime.now(),
chosenPlanState: ChosenPlanStateEnum.stateStarted,
chatId: 'chat-1',
consultantId: 1,
plan: testPlan,
);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
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: 200));
// Обновляем план
final updatedPlan = chosenPlan.copyWith(chosenPlanState: ChosenPlanStateEnum.statePaid);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => updatedPlan);
await chatRoomCubit.updateChosenPlanState();
await Future.delayed(const Duration(milliseconds: 100));
verify(mockChatRepository.getChosenPlan(id: planId)).called(greaterThan(1));
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.chatRoom.chosenPlan?.chosenPlanState, ChosenPlanStateEnum.statePaid);
});
test('updateChosenPlanState - не обновляет если нет chosenPlanId', () async {
const uuid = 'room-uuid';
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: 200));
await chatRoomCubit.updateChosenPlanState();
verifyNever(mockChatRepository.getChosenPlan(id: anyNamed('id')));
});
test('choseStartPlanDate - устанавливает дату начала плана', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
const testPlan = PlanModel(
id: 1,
slug: 'test-plan',
name: 'Test Plan',
image: 'https://example.com/image.jpg',
duration: 7,
state: 'active',
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
details: 'Test details',
packetsCount: 3,
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
isFavorite: false,
minPriceOfOptions: 1000,
maxPriceOfOptions: 2000,
wasLikedByMe: false,
);
final chosenPlan = ChosenPlanModel(
id: planId,
optionType: 'options1',
startDate: DateTime(2024, 1, 1),
chosenPlanState: ChosenPlanStateEnum.statePendingPayment,
chatId: 'chat-1',
consultantId: 1,
plan: testPlan,
);
final updatedPlan = chosenPlan.copyWith(startDate: DateTime(2024, 1, 15));
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
when(mockChatRepository.choseStartPlanDate(id: planId, date: anyNamed('date'))).thenAnswer((_) async => {});
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: 200));
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => updatedPlan);
final result = await chatRoomCubit.choseStartPlanDate(DateTime(2024, 1, 15));
expect(result, true);
verify(mockChatRepository.choseStartPlanDate(id: planId, date: anyNamed('date'))).called(1);
verify(mockChatRepository.getChosenPlan(id: planId)).called(greaterThan(1));
});
test('choseStartPlanDate - возвращает false если state не loaded', () async {
final result = await chatRoomCubit.choseStartPlanDate(DateTime.now());
expect(result, false);
});
test('choseStartPlanDate - возвращает false если нет planId', () async {
const uuid = 'room-uuid';
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: 200));
final result = await chatRoomCubit.choseStartPlanDate(DateTime.now());
expect(result, false);
});
test('cancelChosenPlan - отменяет выбранный план', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
const testPlan = PlanModel(
id: 1,
slug: 'test-plan',
name: 'Test Plan',
image: 'https://example.com/image.jpg',
duration: 7,
state: 'active',
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
details: 'Test details',
packetsCount: 3,
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
isFavorite: false,
minPriceOfOptions: 1000,
maxPriceOfOptions: 2000,
wasLikedByMe: false,
);
final chosenPlan = ChosenPlanModel(
id: planId,
optionType: 'options1',
startDate: DateTime.now(),
chosenPlanState: ChosenPlanStateEnum.stateStarted,
chatId: 'chat-1',
consultantId: 1,
plan: testPlan,
);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
when(mockChatRepository.cancelChosenPlan(id: planId)).thenAnswer((_) async => {});
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: 200));
await chatRoomCubit.cancelChosenPlan(chatId: 'chat-id', planId: planId);
await Future.delayed(const Duration(milliseconds: 100));
verify(mockChatRepository.cancelChosenPlan(id: planId)).called(1);
final state = chatRoomCubit.state as ChatRoomStateLoaded;
expect(state.chatRoom.chosenPlan, null);
});
test('cancelChosenPlan - обрабатывает ошибку', () async {
const uuid = 'room-uuid';
const planId = 123;
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: planId);
const testPlan = PlanModel(
id: 1,
slug: 'test-plan',
name: 'Test Plan',
image: 'https://example.com/image.jpg',
duration: 7,
state: 'active',
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
details: 'Test details',
packetsCount: 3,
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
isFavorite: false,
minPriceOfOptions: 1000,
maxPriceOfOptions: 2000,
wasLikedByMe: false,
);
final chosenPlan = ChosenPlanModel(
id: planId,
optionType: 'options1',
startDate: DateTime.now(),
chosenPlanState: ChosenPlanStateEnum.stateStarted,
chatId: 'chat-1',
consultantId: 1,
plan: testPlan,
);
when(mockChatRepository.getChatRoom(uuid)).thenAnswer((_) async => chatRoomWithPlan);
when(mockChatRepository.getChatMessages(uuid, 1)).thenAnswer((_) async => testChatMessages);
when(mockChatRepository.getChosenPlan(id: planId)).thenAnswer((_) async => chosenPlan);
when(mockChatRepository.cancelChosenPlan(id: planId)).thenThrow(Exception('Error'));
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: 200));
await chatRoomCubit.cancelChosenPlan(chatId: 'chat-id', planId: planId);
await Future.delayed(const Duration(milliseconds: 100));
expect(chatRoomCubit.state, isA<ChatRoomStateError>());
});
});
}

View File

@@ -194,5 +194,114 @@ void main() {
// 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);
});
});
}