forked from eatvio/eatvio_app
feat(tests): updated tests coverage to 66.5%
This commit is contained in:
135
test/features/notifications/models/notification_model_test.dart
Normal file
135
test/features/notifications/models/notification_model_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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>()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user