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

@@ -0,0 +1,135 @@
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('NotificationType.getTypeFromString', () {
test('returns correct type for new string-based keys', () {
final cases = <String, NotificationType>{
'diet-cancelled': NotificationType.dietCancelled,
'entity-commented': NotificationType.entityCommented,
'comment-replied': NotificationType.commentReplied,
'phone-changed': NotificationType.phoneChanged,
'diet-started': NotificationType.dietStarted,
'personal-diet-creation-needed-to-author': NotificationType.personalDietCreationNeeded,
'chosen-plan-client-last-day-finished': NotificationType.chosenPlanClientLastDayFinished,
'chosen-plan-consultant-last-day-started': NotificationType.chosenPlanConsultantLastDayStarted,
'chosen-plan-client-last-day-started': NotificationType.chosenPlanClientLastDayStarted,
'subscription': NotificationType.subscription,
'badge-awarded': NotificationType.badgeAwarded,
'badge-revoked': NotificationType.badgeRevoked,
'diet-expired': NotificationType.dietExpired,
'consultant-request-started': NotificationType.consultantRequestStarted,
'diet-will-started-in': NotificationType.dietWillStartedIn,
'client-close-complaint-success': NotificationType.clientCloseComplaintSuccess,
'complaint-to-moderation-for-user': NotificationType.complaintToModerationForUser,
'client-refund-success': NotificationType.clientRefundSuccess,
'consultant-refund-success': NotificationType.consultantRefundSuccess,
'client-reject-success': NotificationType.clientRejectSuccess,
'consultant-reject-success': NotificationType.consultantRejectSuccess,
'score-added': NotificationType.scoreAdded,
'score-substracted': NotificationType.scoreSubstracted,
'product-status-updated-for-author': NotificationType.productStatusUpdatedForAuthor,
'diet-can-not-started': NotificationType.dietCanNotBeStarted,
'stay-on-track-to-author': NotificationType.stayOnTrackToAuthor,
'chosen-plan-started-to-client': NotificationType.chosenPlanStartedToClient,
'client-start-plan-subscription-to-subscriber': NotificationType.clientStartPlanSubscriptionToSubscriber,
'consultant-confirm-plan-subscription-to-subscriber': NotificationType.consultantConfirmPlanSubscriptionToSubscriber,
'client-paid-plan-subscription-to-author': NotificationType.clientPaidPlanSubscriptionToAuthor,
'chosen-plan-tomorrow-reminder-to-client': NotificationType.chosenPlanTomorrowReminderToClient,
'chosen-plan-tomorrow-reminder-to-consultant': NotificationType.chosenPlanTomorrowReminderToConsultant,
'chosen-plan-started-to-consultant': NotificationType.chosenPlanStartedToConsultant,
'consultant-left-diary-comment': NotificationType.consultantLeftDiaryComment,
'chosen-plan-finished-to-client': NotificationType.chosenPlanFinishedToClient,
'consultant-confirm-plan-subscription-to-author': NotificationType.consultantConfirmPlanSubscriptionToAuthor,
'client-paid-plan-subscription-to-subscriber': NotificationType.clientPaidPlanSubscriptionToSubscriber,
'client-start-plan-subscription-to-author': NotificationType.clientStartPlanSubscriptionToAuthor,
'client-cancel-plan-subscription-to-author': NotificationType.clientCancelPlanSubscriptionToAuthor,
'client-cancel-plan-subscription-to-subscriber': NotificationType.clientCancelPlanSubscriptionToSubscriber,
'user-daily-report-completed': NotificationType.userDailyReportCompleted,
'chosen-plan-finished-to-consultant': NotificationType.chosenPlanFinishedToConsultant,
'stay-on-track-to-customer': NotificationType.stayOnTrackToCustomer,
'diary-reminder-for-users': NotificationType.diaryReminderForUsers,
'personal-diet-is-defined-to-customer': NotificationType.personalDietIsDefinedToCustomer,
};
cases.forEach((key, expected) {
expect(NotificationType.getTypeFromString(key), expected, reason: key);
});
});
test('returns correct type for legacy FQCN values', () {
final cases = <String, NotificationType>{
'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor': NotificationType.planSubscriptionStartedToAuthor,
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor': NotificationType.planSubscriptionCanceledToAuthor,
'App\\Notifications\\Plan\\ChosenPlanStartedToClientNotification': NotificationType.chosenPlanStartedToClientNotification,
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToClientNotification': NotificationType.chosenPlanTomorrowReminderToClientNotification,
'App\\Notifications\\DiaryReminderNotification': NotificationType.diaryReminderNotification,
'App\\Notifications\\Plan\\ConsultantLeftComment': NotificationType.consultantLeftComment,
'App\\Notifications\\Plan\\PersonalizedDietIsDefined': NotificationType.personalizedDietIsDefined,
'App\\Notifications\\Plan\\ChosenPlanFinishedToClient': NotificationType.chosenPlanFinishedToClient,
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToCustomer': NotificationType.planSubscriptionCanceledToCustomer,
'App\\Notifications\\Plan\\ChosenPlanStartedToConsultantNotification': NotificationType.chosenPlanStartedToConsultantNotification,
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToConsultantNotification': NotificationType.chosenPlanTomorrowReminderToConsultantNotification,
'App\\Notifications\\Plan\\PlanSubscriptionPaidToAuthor': NotificationType.planSubscriptionPaidToAuthor,
'App\\Notifications\\Plan\\ChosenPlanFinishedToConsultant': NotificationType.chosenPlanFinishedToConsultant,
'App\\Notifications\\ChosenPlan\\LastDayFinishedForUser': NotificationType.chosenPlanLastDayFinishedForUser,
'App\\Notifications\\ChosenPlan\\LastDayStartedForConsultant': NotificationType.chosenPlanConsultantLastDayStarted,
'App\\Notifications\\ComplaintToModerationNotify': NotificationType.complaintToModerationForUser,
'App\\Notifications\\Comment\\Replied': NotificationType.commentReplied,
'App\\Notifications\\Score\\ScoreAdded': NotificationType.scoreAdded,
'App\\Notifications\\Comment\\EntityCommented': NotificationType.entityCommented,
'App\\Notifications\\Plan\\PlanSubscriptionExpired': NotificationType.planSubscriptionExpired,
'App\\Notifications\\Plan\\PlanSubscriptionRenewed': NotificationType.planSubscriptionRenewed,
'App\\Notifications\\Plan\\ConsultantAssigned': NotificationType.consultantAssigned,
'App\\Notifications\\Plan\\ConsultantUnassigned': NotificationType.consultantUnassigned,
'App\\Notifications\\Payment\\PaymentFailed': NotificationType.paymentFailed,
'App\\Notifications\\Payment\\PaymentSuccessful': NotificationType.paymentSuccessful,
'App\\Notifications\\Goal\\GoalAchieved': NotificationType.goalAchieved,
'App\\Notifications\\Workout\\WorkoutReminder': NotificationType.workoutReminder,
'App\\Notifications\\Meal\\MealReminder': NotificationType.mealReminder,
};
cases.forEach((key, expected) {
expect(NotificationType.getTypeFromString(key), expected, reason: key);
});
});
test('returns unknown for unexpected type', () {
expect(NotificationType.getTypeFromString('some-unknown-type'), NotificationType.unknown);
});
});
group('Notification model factories', () {
test('PlanSubscriptionStartedToAuthorNotification.fromJson parses basic fields', () {
final json = {'plan_name': 'Plan', 'user_name': 'User', 'chat_id': 'chat-1', 'created_at': '2024-01-01T12:00:00Z'};
final notification = PlanSubscriptionStartedToAuthorNotification.fromJson(json, NotificationType.planSubscriptionStartedToAuthor, isRead: true);
expect(notification.planName, 'Plan');
expect(notification.customerName, 'User');
expect(notification.chatId, 'chat-1');
expect(notification.isRead, true);
expect(notification.type, NotificationType.planSubscriptionStartedToAuthor);
});
test('ChosenPlanStartedToClientNotification.fromJson with default isRead', () {
final json = {'plan_name': 'Plan', 'plan_id': 10, 'created_at': '2024-01-01T12:00:00Z'};
final notification = ChosenPlanStartedToClientNotification.fromJson(json, NotificationType.chosenPlanStartedToClient);
expect(notification.planName, 'Plan');
expect(notification.planId, 10);
expect(notification.isRead, false);
expect(notification.type, NotificationType.chosenPlanStartedToClient);
});
test('UnknownNotification uses default createdAt when not provided', () {
final notification = UnknownNotification(type: NotificationType.unknown);
expect(notification.type, NotificationType.unknown);
expect(notification.isRead, true);
// createdAt is initialized with epoch when not provided
expect(notification.createdAt.isBefore(DateTime.now()), isTrue);
});
});
}

View File

@@ -58,6 +58,202 @@ void main() {
verify(mockDio.post(any)).called(1);
});
test('getNotifications - возвращает null при statusCode != 200', () async {
const page = 1;
final response = Response(
data: {},
statusCode: 404,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNull);
});
test('getNotifications - обрабатывает ошибку', () async {
const page = 1;
when(mockDio.get(any)).thenThrow(DioException(
requestOptions: RequestOptions(path: '/api/profile/notifications'),
));
expect(() => notificationsService.getNotifications(page: page), throwsA(isA<DioException>()));
});
test('getNotifications - определяет тип из поля type в корне', () async {
const page = 1;
final response = Response(
data: {
'data': [
{
'type': 'chosen-plan-started-to-client',
'data': {
'plan_name': 'Test Plan',
'plan_id': 1,
},
'read_at': null,
'created_at': '2024-01-01T12:00:00Z',
},
],
'current_page': 1,
'last_page': 1,
'per_page': 10,
'total': 1,
},
statusCode: 200,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNotNull);
expect(result!['notifications'], isA<List>());
expect(result['notifications'].length, 1);
});
test('getNotifications - определяет тип из поля notification_class_name в корне', () async {
const page = 1;
final response = Response(
data: {
'data': [
{
'notification_class_name': 'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor',
'data': {
'plan_name': 'Test Plan',
'user_name': 'Test User',
'chat_id': 'chat-1',
},
'read_at': null,
'created_at': '2024-01-01T12:00:00Z',
},
],
'current_page': 1,
'last_page': 1,
'per_page': 10,
'total': 1,
},
statusCode: 200,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNotNull);
expect(result!['notifications'], isA<List>());
expect(result['notifications'].length, 1);
});
test('getNotifications - определяет тип из поля type в data', () async {
const page = 1;
final response = Response(
data: {
'data': [
{
'data': {
'type': 'diet-started',
'diet_name': 'Test Diet',
},
'read_at': null,
'created_at': '2024-01-01T12:00:00Z',
},
],
'current_page': 1,
'last_page': 1,
'per_page': 10,
'total': 1,
},
statusCode: 200,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNotNull);
expect(result!['notifications'], isA<List>());
expect(result['notifications'].length, 1);
});
test('getNotifications - обрабатывает ошибку парсинга отдельного уведомления', () async {
const page = 1;
final response = Response(
data: {
'data': [
{
'type': 'invalid-type',
'data': null, // Invalid data
'read_at': null,
'created_at': '2024-01-01T12:00:00Z',
},
],
'current_page': 1,
'last_page': 1,
'per_page': 10,
'total': 1,
},
statusCode: 200,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNotNull);
expect(result!['notifications'], isA<List>());
// При ошибке парсинга возвращается UnknownNotification
expect(result['notifications'].length, 1);
});
test('getNotifications - обрабатывает уведомление с read_at', () async {
const page = 1;
final response = Response(
data: {
'data': [
{
'type': 'chosen-plan-started-to-client',
'data': {
'plan_name': 'Test Plan',
'plan_id': 1,
},
'read_at': '2024-01-01T13:00:00Z',
'created_at': '2024-01-01T12:00:00Z',
},
],
'current_page': 1,
'last_page': 1,
'per_page': 10,
'total': 1,
},
statusCode: 200,
requestOptions: RequestOptions(path: '/api/profile/notifications'),
);
when(mockDio.get(any)).thenAnswer((_) async => response);
final result = await notificationsService.getNotifications(page: page);
expect(result, isNotNull);
expect(result!['notifications'], isA<List>());
expect(result['notifications'].length, 1);
});
test('markNotificationsAsRead - обрабатывает ошибку', () async {
when(mockDio.post(any)).thenThrow(DioException(
requestOptions: RequestOptions(path: '/api/profile/mark_notifications_as_readed'),
));
expect(() => notificationsService.markNotificationsAsRead(), throwsA(isA<DioException>()));
});
});
}