forked from eatvio/eatvio_app
Merge remote-tracking branch 'upstream/main' into issue-975
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/routing/route_constants.dart';
|
||||
import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -6,6 +7,50 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
void main() {
|
||||
group('AppRouter', () {
|
||||
group('resolveDeepLinkTarget - finance/alfa/return', () {
|
||||
const orderId = 'alfa_c29_k15138_t1712345678_r1234';
|
||||
|
||||
test('finance/alfa/return/{orderId} → chat-screen?orderId=', () {
|
||||
final target = AppRouter.resolveDeepLinkTarget(
|
||||
Uri.parse('https://eatvio.ru/finance/alfa/return/$orderId'),
|
||||
);
|
||||
|
||||
expect(target, '${RouteConstants.chatScreen}?orderId=$orderId');
|
||||
});
|
||||
|
||||
test('ru/finance/alfa/return/{orderId} → chat-screen?orderId= (metaSegments)', () {
|
||||
final target = AppRouter.resolveDeepLinkTarget(
|
||||
Uri.parse('https://eatvio.ru/ru/finance/alfa/return/$orderId'),
|
||||
);
|
||||
|
||||
expect(target, '${RouteConstants.chatScreen}?orderId=$orderId');
|
||||
});
|
||||
|
||||
test('неполный path /finance/alfa → feed', () {
|
||||
final target = AppRouter.resolveDeepLinkTarget(
|
||||
Uri.parse('https://eatvio.ru/finance/alfa'),
|
||||
);
|
||||
|
||||
expect(target, RouteConstants.feedScreen);
|
||||
});
|
||||
|
||||
test('неполный path /finance → feed', () {
|
||||
final target = AppRouter.resolveDeepLinkTarget(
|
||||
Uri.parse('https://eatvio.ru/finance'),
|
||||
);
|
||||
|
||||
expect(target, RouteConstants.feedScreen);
|
||||
});
|
||||
|
||||
test('регресс: chat/{uuid} → chat-screen?uuid=', () {
|
||||
final target = AppRouter.resolveDeepLinkTarget(
|
||||
Uri.parse('https://eatvio.ru/ru/chat/uuid-1'),
|
||||
);
|
||||
|
||||
expect(target, '${RouteConstants.chatScreen}?uuid=uuid-1');
|
||||
});
|
||||
});
|
||||
|
||||
test('navigatorKey инициализирован', () {
|
||||
expect(AppRouter.navigatorKey, isNotNull);
|
||||
expect(AppRouter.navigatorKey, isA<GlobalKey<NavigatorState>>());
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:eatvio_app/features/chat/models/order_id_to_conversation_model.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../fixtures/fixture_reader.dart';
|
||||
|
||||
void main() {
|
||||
group('OrderIdToConversationModel', () {
|
||||
test('fromJson создаёт модель из JSON фикстуры', () {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/payments/order_id_to_chat_uuid_response.json',
|
||||
);
|
||||
|
||||
final model = OrderIdToConversationModel.fromJson(json);
|
||||
|
||||
expect(model.chosenPlanId, 12);
|
||||
expect(
|
||||
model.conversationUuid,
|
||||
'fbd81429-8019-499a-80e8-e3933cc4d872',
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,25 +1,47 @@
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../fixtures/fixture_reader.dart';
|
||||
|
||||
void main() {
|
||||
group('PaymentModel', () {
|
||||
test('создает PaymentModel с правильными значениями', () {
|
||||
final model = PaymentModel(
|
||||
paymentUrl: 'https://example.com/payment',
|
||||
);
|
||||
final model = PaymentModel(paymentUrl: 'https://example.com/payment');
|
||||
|
||||
expect(model.paymentUrl, 'https://example.com/payment');
|
||||
});
|
||||
|
||||
test('fromJson создает PaymentModel из JSON', () {
|
||||
final json = {
|
||||
'checkout_url': 'https://example.com/payment',
|
||||
};
|
||||
final json = {'checkout_url': 'https://example.com/payment'};
|
||||
|
||||
final model = PaymentModel.fromJson(json);
|
||||
|
||||
expect(model.paymentUrl, 'https://example.com/payment');
|
||||
});
|
||||
|
||||
test(
|
||||
'fromJson парсит ответ alfa-bank (checkout_url, order_id, payment_provider)',
|
||||
() {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response_alfa_bank.json',
|
||||
);
|
||||
|
||||
final model = PaymentModel.fromJson(json);
|
||||
|
||||
expect(model.paymentUrl, 'https://qr.nspk.ru/ASDFASDFASDFASDF');
|
||||
expect(model.orderId, 'alfa_c29_k15138_t1712345678_r1234');
|
||||
expect(model.paymentProvider, 'alfa-bank');
|
||||
},
|
||||
);
|
||||
|
||||
test('fromJson парсит mock-ответ (checkout_url без order_id)', () {
|
||||
final json = FixtureReader.readAsMap('chat/payments/payment_response.json');
|
||||
|
||||
final model = PaymentModel.fromJson(json);
|
||||
|
||||
expect(model.paymentUrl, 'https://example.com/checkout/payment-123');
|
||||
expect(model.orderId, isNull);
|
||||
expect(model.paymentProvider, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
|
||||
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/my_finance/models/card_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/order_id_to_conversation_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
@@ -102,6 +103,89 @@ void main() {
|
||||
// Let's check that at least the repository methods were called
|
||||
verify(mockChatRepository.getChatRoom(uuid)).called(1);
|
||||
verify(mockChatRepository.getChatMessages(uuid, 1)).called(1);
|
||||
verifyNever(
|
||||
mockChatRepository.resolveOrderIdToConversation(orderId: anyNamed('orderId')),
|
||||
);
|
||||
});
|
||||
|
||||
test('initChat - с orderId резолвит conversation и инициализирует чат', () async {
|
||||
const orderId = 'alfa_c29_k15138_t1712345678_r1234';
|
||||
const conversationUuid = 'fbd81429-8019-499a-80e8-e3933cc4d872';
|
||||
const chosenPlanId = 12;
|
||||
final resolved = OrderIdToConversationModel(
|
||||
chosenPlanId: chosenPlanId,
|
||||
conversationUuid: conversationUuid,
|
||||
);
|
||||
final chatRoomWithPlan = testChatRoom.copyWith(chosenPlanId: chosenPlanId);
|
||||
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: chosenPlanId,
|
||||
optionType: 'options1',
|
||||
startDate: DateTime.now(),
|
||||
chosenPlanState: ChosenPlanStateEnum.statePendingPayment,
|
||||
chatId: 'chat-1',
|
||||
consultantId: 2,
|
||||
plan: testPlan,
|
||||
);
|
||||
|
||||
when(
|
||||
mockChatRepository.resolveOrderIdToConversation(orderId: orderId),
|
||||
).thenAnswer((_) async => resolved);
|
||||
when(mockChatRepository.getChatRoom(conversationUuid)).thenAnswer((_) async => chatRoomWithPlan);
|
||||
when(mockChatRepository.getChatMessages(conversationUuid, 1)).thenAnswer((_) async => testChatMessages);
|
||||
when(mockChatRepository.getChosenPlan(id: chosenPlanId)).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(null, orderId: orderId);
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
verify(mockChatRepository.resolveOrderIdToConversation(orderId: orderId)).called(1);
|
||||
verify(mockChatRepository.getChatRoom(conversationUuid)).called(1);
|
||||
verify(mockChatRepository.getChosenPlan(id: chosenPlanId)).called(1);
|
||||
verify(mockChatRepository.getChatMessages(conversationUuid, 1)).called(1);
|
||||
expect(chatRoomCubit.state, isA<ChatRoomStateLoaded>());
|
||||
final loaded = chatRoomCubit.state as ChatRoomStateLoaded;
|
||||
expect(loaded.chatRoom.chosenPlan?.id, chosenPlanId);
|
||||
});
|
||||
|
||||
test('initChat - при ошибке resolve не вызывает getChatRoom', () async {
|
||||
const orderId = 'alfa_c29_k15138_t1712345678_r1234';
|
||||
when(
|
||||
mockChatRepository.resolveOrderIdToConversation(orderId: orderId),
|
||||
).thenThrow(Exception('Resolve failed'));
|
||||
|
||||
await chatRoomCubit.initChat(null, orderId: orderId);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
|
||||
expect(chatRoomCubit.state, isA<ChatRoomStateError>());
|
||||
verifyNever(mockChatRepository.getChatRoom(any));
|
||||
});
|
||||
|
||||
test('initChat - обрабатывает ошибку', () async {
|
||||
@@ -678,7 +762,7 @@ void main() {
|
||||
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.getPaymentLink(id: planId)).thenAnswer((_) async => PaymentModel(paymentUrl: paymentUrl));
|
||||
when(mockChatRepository.getPaymentLink(id: planId, paymentProvider: 'alfa-bank')).thenAnswer((_) async => PaymentModel(paymentUrl: paymentUrl));
|
||||
when(mockWsManager.client).thenReturn(null);
|
||||
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
|
||||
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
|
||||
@@ -691,7 +775,7 @@ void main() {
|
||||
final result = await chatRoomCubit.getPaymentLink();
|
||||
|
||||
expect(result, paymentUrl);
|
||||
verify(mockChatRepository.getPaymentLink(id: planId)).called(1);
|
||||
verify(mockChatRepository.getPaymentLink(id: planId, paymentProvider: 'alfa-bank')).called(1);
|
||||
});
|
||||
|
||||
test('getPaymentLink - обрабатывает ошибку', () async {
|
||||
@@ -730,7 +814,7 @@ void main() {
|
||||
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.getPaymentLink(id: planId)).thenThrow(Exception('Payment error'));
|
||||
when(mockChatRepository.getPaymentLink(id: planId, paymentProvider: 'alfa-bank')).thenThrow(Exception('Payment error'));
|
||||
when(mockWsManager.client).thenReturn(null);
|
||||
when(mockWsManager.initPrivateDirectChatChannel(any)).thenAnswer((_) async => Future.value());
|
||||
when(mockWsManager.subscribeDirectChatChannel()).thenReturn(null);
|
||||
|
||||
@@ -149,7 +149,8 @@ void main() {
|
||||
when(chatRoomCubit.state).thenReturn(state);
|
||||
when(chatRoomCubit.stream).thenAnswer((_) => const Stream<ChatRoomState>.empty());
|
||||
when(chatRoomCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(chatRoomCubit.initChat(any, sendInitialMessage: anyNamed('sendInitialMessage'))).thenAnswer((_) async => Future<void>.value());
|
||||
when(chatRoomCubit.initChat(any, sendInitialMessage: anyNamed('sendInitialMessage'), orderId: anyNamed('orderId')))
|
||||
.thenAnswer((_) async => 'uuid');
|
||||
when(chatRoomCubit.setTimer()).thenReturn(null);
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ void main() {
|
||||
when(cubit.state).thenReturn(const ChatRoomState.loading());
|
||||
when(cubit.stream).thenAnswer((_) => const Stream<ChatRoomState>.empty());
|
||||
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(cubit.initChat(any, sendInitialMessage: anyNamed('sendInitialMessage'))).thenAnswer((_) async => Future<void>.value());
|
||||
when(cubit.initChat(any, sendInitialMessage: anyNamed('sendInitialMessage'), orderId: anyNamed('orderId')))
|
||||
.thenAnswer((_) async => 'uuid');
|
||||
when(cubit.setTimer()).thenReturn(null);
|
||||
sl.registerFactory<ChatRoomCubit>(() => cubit);
|
||||
|
||||
|
||||
@@ -5,12 +5,15 @@ import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/core/models/pagination_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_model.dart' as chat_room;
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_model.dart'
|
||||
as chat_room;
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/order_id_to_conversation_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/plan_complain_model.dart';
|
||||
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
|
||||
import 'package:eatvio_app/features/chat/services/chat_service.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';
|
||||
@@ -32,7 +35,9 @@ void main() {
|
||||
});
|
||||
|
||||
test('emitNewMessageToChats - эмитит новое сообщение в поток', () async {
|
||||
final messageFixture = FixtureReader.readAsMap('chat/chat_message_response.json');
|
||||
final messageFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_message_response.json',
|
||||
);
|
||||
final message = ChatMessageModel.fromJson(messageFixture);
|
||||
|
||||
chatRepository.emitNewMessageToChats(message);
|
||||
@@ -45,12 +50,14 @@ void main() {
|
||||
const data = 'Test message';
|
||||
const socketId = 'socket-id-123';
|
||||
|
||||
when(mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: null,
|
||||
socketId: socketId,
|
||||
)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: null,
|
||||
socketId: socketId,
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
// Note: SocketIdStorage is static and can't be easily mocked
|
||||
// This test verifies the repository calls the service correctly
|
||||
@@ -68,12 +75,14 @@ void main() {
|
||||
const data = 'Test message with files';
|
||||
const socketId = 'socket-id-123';
|
||||
|
||||
when(mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: anyNamed('mediaData'),
|
||||
socketId: socketId,
|
||||
)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: anyNamed('mediaData'),
|
||||
socketId: socketId,
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
// Создаем временные файлы для теста
|
||||
final tempFile1 = File('test1.jpg');
|
||||
@@ -113,11 +122,14 @@ void main() {
|
||||
|
||||
test('getChatRoom - получает комнату чата по UUID', () async {
|
||||
const uuid = 'chat-1';
|
||||
final roomFixture = FixtureReader.readAsMap('chat/chat_room_response.json');
|
||||
final roomFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_response.json',
|
||||
);
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
|
||||
when(mockChatService.getChatRoom(uuid: uuid))
|
||||
.thenAnswer((_) async => expectedRoom);
|
||||
when(
|
||||
mockChatService.getChatRoom(uuid: uuid),
|
||||
).thenAnswer((_) async => expectedRoom);
|
||||
|
||||
final result = await chatRepository.getChatRoom(uuid);
|
||||
|
||||
@@ -132,18 +144,20 @@ void main() {
|
||||
|
||||
when(mockChatService.getChatRoom(uuid: uuid)).thenThrow(error);
|
||||
|
||||
expect(
|
||||
() => chatRepository.getChatRoom(uuid),
|
||||
throwsException,
|
||||
);
|
||||
expect(() => chatRepository.getChatRoom(uuid), throwsException);
|
||||
});
|
||||
|
||||
test('getChatRooms - получает список комнат чата', () async {
|
||||
final paginationFixture = FixtureReader.readAsMap('chat/chat_room_pagination_response.json');
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(paginationFixture);
|
||||
final paginationFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_pagination_response.json',
|
||||
);
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(
|
||||
paginationFixture,
|
||||
);
|
||||
|
||||
when(mockChatService.getChatRooms(query: null, page: 1))
|
||||
.thenAnswer((_) async => expectedPagination);
|
||||
when(
|
||||
mockChatService.getChatRooms(query: null, page: 1),
|
||||
).thenAnswer((_) async => expectedPagination);
|
||||
|
||||
final result = await chatRepository.getChatRooms();
|
||||
|
||||
@@ -154,11 +168,16 @@ void main() {
|
||||
|
||||
test('getChatRooms - получает список комнат с запросом', () async {
|
||||
const query = 'test query';
|
||||
final paginationFixture = FixtureReader.readAsMap('chat/chat_room_pagination_response.json');
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(paginationFixture);
|
||||
final paginationFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_pagination_response.json',
|
||||
);
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(
|
||||
paginationFixture,
|
||||
);
|
||||
|
||||
when(mockChatService.getChatRooms(query: query, page: 1))
|
||||
.thenAnswer((_) async => expectedPagination);
|
||||
when(
|
||||
mockChatService.getChatRooms(query: query, page: 1),
|
||||
).thenAnswer((_) async => expectedPagination);
|
||||
|
||||
final result = await chatRepository.getChatRooms(query: query);
|
||||
|
||||
@@ -181,8 +200,9 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
when(mockChatService.getChatRooms(query: null, page: 2))
|
||||
.thenAnswer((_) async => expectedPagination);
|
||||
when(
|
||||
mockChatService.getChatRooms(query: null, page: 2),
|
||||
).thenAnswer((_) async => expectedPagination);
|
||||
|
||||
final result = await chatRepository.getChatRooms(page: 2);
|
||||
|
||||
@@ -193,11 +213,14 @@ void main() {
|
||||
test('getChatMessages - получает сообщения чата', () async {
|
||||
const chatId = 'chat-1';
|
||||
const page = 1;
|
||||
final messagesFixture = FixtureReader.readAsMap('chat/chat_messages_response.json');
|
||||
final messagesFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_messages_response.json',
|
||||
);
|
||||
final expectedMessages = ChatMessagesModel.fromJson(messagesFixture);
|
||||
|
||||
when(mockChatService.getChatMessages(id: chatId, page: page))
|
||||
.thenAnswer((_) async => expectedMessages);
|
||||
when(
|
||||
mockChatService.getChatMessages(id: chatId, page: page),
|
||||
).thenAnswer((_) async => expectedMessages);
|
||||
|
||||
final result = await chatRepository.getChatMessages(chatId, page);
|
||||
|
||||
@@ -206,42 +229,53 @@ void main() {
|
||||
verify(mockChatService.getChatMessages(id: chatId, page: page)).called(1);
|
||||
});
|
||||
|
||||
test('getChatRoomIdByUserSlug - получает комнату по slug пользователя', () async {
|
||||
const slug = 'user-1';
|
||||
final roomFixture = FixtureReader.readAsMap('chat/chat_room_response.json');
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
test(
|
||||
'getChatRoomIdByUserSlug - получает комнату по slug пользователя',
|
||||
() async {
|
||||
const slug = 'user-1';
|
||||
final roomFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_response.json',
|
||||
);
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
|
||||
when(mockChatService.getChatRoomIdByUserSlug(user: slug))
|
||||
.thenAnswer((_) async => expectedRoom);
|
||||
when(
|
||||
mockChatService.getChatRoomIdByUserSlug(user: slug),
|
||||
).thenAnswer((_) async => expectedRoom);
|
||||
|
||||
final result = await chatRepository.getChatRoomIdByUserSlug(slug: slug);
|
||||
final result = await chatRepository.getChatRoomIdByUserSlug(slug: slug);
|
||||
|
||||
expect(result, isA<chat_room.ChatRoomModel>());
|
||||
verify(mockChatService.getChatRoomIdByUserSlug(user: slug)).called(1);
|
||||
});
|
||||
expect(result, isA<chat_room.ChatRoomModel>());
|
||||
verify(mockChatService.getChatRoomIdByUserSlug(user: slug)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('markMessagesAsRead - отмечает сообщения как прочитанные', () async {
|
||||
const chatId = 'chat-id-123';
|
||||
final messageIds = [1, 2, 3];
|
||||
|
||||
when(mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await chatRepository.markMessagesAsRead(chatId, messageIds);
|
||||
|
||||
verify(mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
)).called(1);
|
||||
verify(
|
||||
mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('cancelChosenPlan - отменяет выбранный план', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(mockChatService.cancelChosenPlan(id: planId))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.cancelChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await chatRepository.cancelChosenPlan(id: planId);
|
||||
|
||||
@@ -252,36 +286,114 @@ void main() {
|
||||
const planId = 123;
|
||||
final date = {'start_date': '2024-01-15'};
|
||||
|
||||
when(mockChatService.choseStartPlanDate(id: planId, date: date))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.choseStartPlanDate(id: planId, date: date),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await chatRepository.choseStartPlanDate(id: planId, date: date);
|
||||
|
||||
verify(mockChatService.choseStartPlanDate(id: planId, date: date)).called(1);
|
||||
verify(
|
||||
mockChatService.choseStartPlanDate(id: planId, date: date),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('getPaymentLink - получает ссылку на оплату', () async {
|
||||
const planId = 123;
|
||||
final paymentFixture = FixtureReader.readAsMap('chat/payment_response.json');
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
|
||||
when(mockChatService.getPaymentLink(id: planId))
|
||||
.thenAnswer((_) async => expectedPayment);
|
||||
when(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'mock'},
|
||||
),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
final result = await chatRepository.getPaymentLink(id: planId);
|
||||
final result = await chatRepository.getPaymentLink(
|
||||
id: planId,
|
||||
paymentProvider: 'mock',
|
||||
);
|
||||
|
||||
expect(result, isA<PaymentModel>());
|
||||
expect(result.paymentUrl, 'https://example.com/checkout/payment-123');
|
||||
verify(mockChatService.getPaymentLink(id: planId)).called(1);
|
||||
verify(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'mock'},
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'getPaymentLink - передаёт payment_provider в тело запроса alfa-bank',
|
||||
() async {
|
||||
const planId = 123;
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response_alfa_bank.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
|
||||
when(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
final result = await chatRepository.getPaymentLink(
|
||||
id: planId,
|
||||
paymentProvider: 'alfa-bank',
|
||||
);
|
||||
|
||||
expect(result, isA<PaymentModel>());
|
||||
expect(result.paymentUrl, 'https://qr.nspk.ru/ASDFASDFASDFASDF');
|
||||
expect(result.orderId, 'alfa_c29_k15138_t1712345678_r1234');
|
||||
expect(result.paymentProvider, 'alfa-bank');
|
||||
verify(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('resolveOrderIdToConversation - получает модель по orderId', () async {
|
||||
const orderId = 'alfa_c29_k15138_t1712345678_r1234';
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/payments/order_id_to_chat_uuid_response.json',
|
||||
);
|
||||
final expected = OrderIdToConversationModel.fromJson(fixture);
|
||||
|
||||
when(
|
||||
mockChatService.resolveOrderIdToConversation(orderId: orderId),
|
||||
).thenAnswer((_) async => expected);
|
||||
|
||||
final result = await chatRepository.resolveOrderIdToConversation(
|
||||
orderId: orderId,
|
||||
);
|
||||
|
||||
expect(result, isA<OrderIdToConversationModel>());
|
||||
expect(result.chosenPlanId, 12);
|
||||
expect(result.conversationUuid, "fbd81429-8019-499a-80e8-e3933cc4d872");
|
||||
|
||||
verify(
|
||||
mockChatService.resolveOrderIdToConversation(orderId: orderId),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('getChosenPlan - получает выбранный план', () async {
|
||||
const planId = 1;
|
||||
final chosenPlanFixture = FixtureReader.readAsMap('chat/chosen_plan_response.json');
|
||||
final chosenPlanFixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_in_progress_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(chosenPlanFixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId))
|
||||
.thenAnswer((_) async => expectedPlan);
|
||||
when(
|
||||
mockChatService.getChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await chatRepository.getChosenPlan(id: planId);
|
||||
|
||||
@@ -290,66 +402,140 @@ void main() {
|
||||
verify(mockChatService.getChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
group('getChosenPlan payment polling statuses', () {
|
||||
test('getChosenPlan - возвращает план в статусе pending_payment', () async {
|
||||
const planId = 1;
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_pending_payment_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(fixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId)).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await chatRepository.getChosenPlan(id: planId);
|
||||
|
||||
expect(result.chosenPlanState, ChosenPlanStateEnum.statePendingPayment);
|
||||
verify(mockChatService.getChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
test('getChosenPlan - возвращает план в статусе paid', () async {
|
||||
const planId = 1;
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_paid_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(fixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId)).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await chatRepository.getChosenPlan(id: planId);
|
||||
|
||||
expect(result.chosenPlanState, ChosenPlanStateEnum.statePaid);
|
||||
verify(mockChatService.getChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
test('getChosenPlan - возвращает план в статусе payment_failed', () async {
|
||||
const planId = 1;
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_payment_failed_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(fixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId)).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await chatRepository.getChosenPlan(id: planId);
|
||||
|
||||
expect(result.chosenPlanState, ChosenPlanStateEnum.statePaymentFailed);
|
||||
verify(mockChatService.getChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
test('getChosenPlan - возвращает план в статусе canceled_by_the_initiator', () async {
|
||||
const planId = 1;
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_canceled_by_the_initiator_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(fixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId)).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await chatRepository.getChosenPlan(id: planId);
|
||||
|
||||
expect(result.chosenPlanState, ChosenPlanStateEnum.stateCanceledByTheInitiator);
|
||||
verify(mockChatService.getChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
test('finishChosenPlan - завершает выбранный план', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(mockChatService.finishChosenPlan(id: planId))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.finishChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await chatRepository.finishChosenPlan(id: planId);
|
||||
|
||||
verify(mockChatService.finishChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
test('complainAboutConsultation - отправляет жалобу на консультацию', () async {
|
||||
const planId = 123;
|
||||
final complainFixture = FixtureReader.readAsMap('chat/plan_complain_response.json');
|
||||
final complainModel = PlanComplainModel.fromJson(complainFixture);
|
||||
test(
|
||||
'complainAboutConsultation - отправляет жалобу на консультацию',
|
||||
() async {
|
||||
const planId = 123;
|
||||
final complainFixture = FixtureReader.readAsMap(
|
||||
'chat/plan_complain_response.json',
|
||||
);
|
||||
final complainModel = PlanComplainModel.fromJson(complainFixture);
|
||||
|
||||
when(mockChatService.complainAboutConsultation(id: planId, body: complainModel))
|
||||
.thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await chatRepository.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
);
|
||||
await chatRepository.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
);
|
||||
|
||||
verify(mockChatService.complainAboutConsultation(id: planId, body: complainModel))
|
||||
.called(1);
|
||||
});
|
||||
verify(
|
||||
mockChatService.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('getChatByUserSlug - возвращает ID чата при успешном ответе', () async {
|
||||
const slug = 'user-slug';
|
||||
const chatId = 'chat-id-123';
|
||||
final response = HttpResponse(
|
||||
{'id': chatId},
|
||||
Response(
|
||||
requestOptions: RequestOptions(path: ''),
|
||||
statusCode: 200,
|
||||
),
|
||||
);
|
||||
test(
|
||||
'getChatByUserSlug - возвращает ID чата при успешном ответе',
|
||||
() async {
|
||||
const slug = 'user-slug';
|
||||
const chatId = 'chat-id-123';
|
||||
final response = HttpResponse({
|
||||
'id': chatId,
|
||||
}, Response(requestOptions: RequestOptions(path: ''), statusCode: 200));
|
||||
|
||||
when(mockChatService.getChatByUserSlug(slug: slug))
|
||||
.thenAnswer((_) async => response);
|
||||
when(
|
||||
mockChatService.getChatByUserSlug(slug: slug),
|
||||
).thenAnswer((_) async => response);
|
||||
|
||||
final result = await chatRepository.getChatByUserSlug(slug);
|
||||
final result = await chatRepository.getChatByUserSlug(slug);
|
||||
|
||||
expect(result, chatId);
|
||||
verify(mockChatService.getChatByUserSlug(slug: slug)).called(1);
|
||||
});
|
||||
expect(result, chatId);
|
||||
verify(mockChatService.getChatByUserSlug(slug: slug)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('getChatByUserSlug - возвращает 404 при отсутствии чата', () async {
|
||||
const slug = 'user-slug';
|
||||
final response = HttpResponse(
|
||||
{},
|
||||
Response(
|
||||
requestOptions: RequestOptions(path: ''),
|
||||
statusCode: 404,
|
||||
),
|
||||
Response(requestOptions: RequestOptions(path: ''), statusCode: 404),
|
||||
);
|
||||
|
||||
when(mockChatService.getChatByUserSlug(slug: slug))
|
||||
.thenAnswer((_) async => response);
|
||||
when(
|
||||
mockChatService.getChatByUserSlug(slug: slug),
|
||||
).thenAnswer((_) async => response);
|
||||
|
||||
final result = await chatRepository.getChatByUserSlug(slug);
|
||||
|
||||
@@ -357,17 +543,20 @@ void main() {
|
||||
verify(mockChatService.getChatByUserSlug(slug: slug)).called(1);
|
||||
});
|
||||
|
||||
test('getChatByUserSlug - пробрасывает исключение при ошибке сети', () async {
|
||||
const slug = 'user-slug';
|
||||
test(
|
||||
'getChatByUserSlug - пробрасывает исключение при ошибке сети',
|
||||
() async {
|
||||
const slug = 'user-slug';
|
||||
|
||||
when(mockChatService.getChatByUserSlug(slug: slug))
|
||||
.thenThrow(Exception('Network error'));
|
||||
when(
|
||||
mockChatService.getChatByUserSlug(slug: slug),
|
||||
).thenThrow(Exception('Network error'));
|
||||
|
||||
await expectLater(
|
||||
chatRepository.getChatByUserSlug(slug),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
await expectLater(
|
||||
chatRepository.getChatByUserSlug(slug),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_model.dart' as chat_room;
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_model.dart'
|
||||
as chat_room;
|
||||
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/order_id_to_conversation_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/chat/models/plan_complain_model.dart';
|
||||
import 'package:eatvio_app/features/chat/services/chat_service.dart';
|
||||
@@ -24,10 +26,16 @@ void main() {
|
||||
});
|
||||
|
||||
test('getChatRooms - возвращает список комнат чата', () async {
|
||||
final paginationFixture = FixtureReader.readAsMap('chat/chat_room_pagination_response.json');
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(paginationFixture);
|
||||
final paginationFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_pagination_response.json',
|
||||
);
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(
|
||||
paginationFixture,
|
||||
);
|
||||
|
||||
when(mockChatService.getChatRooms(query: null, page: 1)).thenAnswer((_) async => expectedPagination);
|
||||
when(
|
||||
mockChatService.getChatRooms(query: null, page: 1),
|
||||
).thenAnswer((_) async => expectedPagination);
|
||||
|
||||
final result = await mockChatService.getChatRooms(query: null, page: 1);
|
||||
|
||||
@@ -38,10 +46,16 @@ void main() {
|
||||
|
||||
test('getChatRooms - возвращает список комнат с запросом', () async {
|
||||
const query = 'test query';
|
||||
final paginationFixture = FixtureReader.readAsMap('chat/chat_room_pagination_response.json');
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(paginationFixture);
|
||||
final paginationFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_pagination_response.json',
|
||||
);
|
||||
final expectedPagination = ChatRoomPaginationModel.fromJson(
|
||||
paginationFixture,
|
||||
);
|
||||
|
||||
when(mockChatService.getChatRooms(query: query, page: 1)).thenAnswer((_) async => expectedPagination);
|
||||
when(
|
||||
mockChatService.getChatRooms(query: query, page: 1),
|
||||
).thenAnswer((_) async => expectedPagination);
|
||||
|
||||
final result = await mockChatService.getChatRooms(query: query, page: 1);
|
||||
|
||||
@@ -51,10 +65,14 @@ void main() {
|
||||
|
||||
test('getChatRoom - возвращает комнату чата по UUID', () async {
|
||||
const uuid = 'chat-1';
|
||||
final roomFixture = FixtureReader.readAsMap('chat/chat_room_response.json');
|
||||
final roomFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_response.json',
|
||||
);
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
|
||||
when(mockChatService.getChatRoom(uuid: uuid)).thenAnswer((_) async => expectedRoom);
|
||||
when(
|
||||
mockChatService.getChatRoom(uuid: uuid),
|
||||
).thenAnswer((_) async => expectedRoom);
|
||||
|
||||
final result = await mockChatService.getChatRoom(uuid: uuid);
|
||||
|
||||
@@ -67,62 +85,114 @@ void main() {
|
||||
const slug = 'user-slug';
|
||||
const data = 'Test message';
|
||||
const socketId = 'socket-id-123';
|
||||
final messageFixture = FixtureReader.readAsMap('chat/chat_message_response.json');
|
||||
final messageFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_message_response.json',
|
||||
);
|
||||
final expectedMessage = ChatMessageModel.fromJson(messageFixture);
|
||||
|
||||
// ignore: null_argument_to_non_null_type
|
||||
when(mockChatService.sendMessage(slug: slug, data: data, mediaData: null, socketId: socketId)).thenAnswer((_) async => expectedMessage);
|
||||
when(
|
||||
mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: null,
|
||||
socketId: socketId,
|
||||
),
|
||||
).thenAnswer((_) async => expectedMessage);
|
||||
|
||||
final result = await mockChatService.sendMessage(slug: slug, data: data, mediaData: null, socketId: socketId);
|
||||
final result = await mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: null,
|
||||
socketId: socketId,
|
||||
);
|
||||
|
||||
expect(result, isA<ChatMessageModel>());
|
||||
expect(result.data, data);
|
||||
verify(mockChatService.sendMessage(slug: slug, data: data, mediaData: null, socketId: socketId)).called(1);
|
||||
verify(
|
||||
mockChatService.sendMessage(
|
||||
slug: slug,
|
||||
data: data,
|
||||
mediaData: null,
|
||||
socketId: socketId,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('getChatMessages - возвращает сообщения чата', () async {
|
||||
const chatId = 'chat-1';
|
||||
const page = 1;
|
||||
final messagesFixture = FixtureReader.readAsMap('chat/chat_messages_response.json');
|
||||
final messagesFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_messages_response.json',
|
||||
);
|
||||
final expectedMessages = ChatMessagesModel.fromJson(messagesFixture);
|
||||
|
||||
when(mockChatService.getChatMessages(id: chatId, page: page)).thenAnswer((_) async => expectedMessages);
|
||||
when(
|
||||
mockChatService.getChatMessages(id: chatId, page: page),
|
||||
).thenAnswer((_) async => expectedMessages);
|
||||
|
||||
final result = await mockChatService.getChatMessages(id: chatId, page: page);
|
||||
final result = await mockChatService.getChatMessages(
|
||||
id: chatId,
|
||||
page: page,
|
||||
);
|
||||
|
||||
expect(result, isA<ChatMessagesModel>());
|
||||
expect(result.messagesList.length, 2);
|
||||
verify(mockChatService.getChatMessages(id: chatId, page: page)).called(1);
|
||||
});
|
||||
|
||||
test('getChatRoomIdByUserSlug - возвращает комнату по slug пользователя', () async {
|
||||
const slug = 'user-1';
|
||||
final roomFixture = FixtureReader.readAsMap('chat/chat_room_response.json');
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
test(
|
||||
'getChatRoomIdByUserSlug - возвращает комнату по slug пользователя',
|
||||
() async {
|
||||
const slug = 'user-1';
|
||||
final roomFixture = FixtureReader.readAsMap(
|
||||
'chat/chat_room_response.json',
|
||||
);
|
||||
final expectedRoom = chat_room.ChatRoomModel.fromJson(roomFixture);
|
||||
|
||||
when(mockChatService.getChatRoomIdByUserSlug(user: slug)).thenAnswer((_) async => expectedRoom);
|
||||
when(
|
||||
mockChatService.getChatRoomIdByUserSlug(user: slug),
|
||||
).thenAnswer((_) async => expectedRoom);
|
||||
|
||||
final result = await mockChatService.getChatRoomIdByUserSlug(user: slug);
|
||||
final result = await mockChatService.getChatRoomIdByUserSlug(
|
||||
user: slug,
|
||||
);
|
||||
|
||||
expect(result, isA<chat_room.ChatRoomModel>());
|
||||
verify(mockChatService.getChatRoomIdByUserSlug(user: slug)).called(1);
|
||||
});
|
||||
expect(result, isA<chat_room.ChatRoomModel>());
|
||||
verify(mockChatService.getChatRoomIdByUserSlug(user: slug)).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('markMessagesAsRead - отмечает сообщения как прочитанные', () async {
|
||||
const chatId = 'chat-id-123';
|
||||
final messageIds = [1, 2, 3];
|
||||
|
||||
when(mockChatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds})).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await mockChatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds});
|
||||
await mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
);
|
||||
|
||||
verify(mockChatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds})).called(1);
|
||||
verify(
|
||||
mockChatService.markMessagesAsRead(
|
||||
id: chatId,
|
||||
body: {'messages_ids': messageIds},
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('cancelChosenPlan - отменяет выбранный план', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(mockChatService.cancelChosenPlan(id: planId)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.cancelChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await mockChatService.cancelChosenPlan(id: planId);
|
||||
|
||||
@@ -133,33 +203,80 @@ void main() {
|
||||
const planId = 123;
|
||||
final date = {'start_date': '2024-01-15'};
|
||||
|
||||
when(mockChatService.choseStartPlanDate(id: planId, date: date)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.choseStartPlanDate(id: planId, date: date),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await mockChatService.choseStartPlanDate(id: planId, date: date);
|
||||
|
||||
verify(mockChatService.choseStartPlanDate(id: planId, date: date)).called(1);
|
||||
verify(
|
||||
mockChatService.choseStartPlanDate(id: planId, date: date),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('getPaymentLink - возвращает ссылку на оплату', () async {
|
||||
const planId = 123;
|
||||
final paymentFixture = FixtureReader.readAsMap('chat/payment_response.json');
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
|
||||
when(mockChatService.getPaymentLink(id: planId)).thenAnswer((_) async => expectedPayment);
|
||||
when(
|
||||
mockChatService.getPaymentLink(id: planId, body: {'payment_provider': 'mock'}),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
final result = await mockChatService.getPaymentLink(id: planId);
|
||||
final result = await mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'mock'},
|
||||
);
|
||||
|
||||
expect(result, isA<PaymentModel>());
|
||||
expect(result.paymentUrl, 'https://example.com/checkout/payment-123');
|
||||
verify(mockChatService.getPaymentLink(id: planId)).called(1);
|
||||
verify(mockChatService.getPaymentLink(id: planId, body: {'payment_provider': 'mock'})).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'getPaymentLink - возвращает ссылку на оплату alfa-bank',
|
||||
() async {
|
||||
const planId = 123;
|
||||
final requestFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_alfabank_request.json',
|
||||
);
|
||||
final requestBody = Map<String, String>.from(requestFixture);
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response_alfa_bank.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
|
||||
when(
|
||||
mockChatService.getPaymentLink(id: planId, body: requestBody),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
final result = await mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
expect(result, isA<PaymentModel>());
|
||||
expect(result.paymentUrl, 'https://qr.nspk.ru/ASDFASDFASDFASDF');
|
||||
expect(result.orderId, 'alfa_c29_k15138_t1712345678_r1234');
|
||||
expect(result.paymentProvider, 'alfa-bank');
|
||||
verify(
|
||||
mockChatService.getPaymentLink(id: planId, body: requestBody),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('getChosenPlan - возвращает выбранный план', () async {
|
||||
const planId = 1;
|
||||
final chosenPlanFixture = FixtureReader.readAsMap('chat/chosen_plan_response.json');
|
||||
final chosenPlanFixture = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_in_progress_response.json',
|
||||
);
|
||||
final expectedPlan = ChosenPlanModel.fromJson(chosenPlanFixture);
|
||||
|
||||
when(mockChatService.getChosenPlan(id: planId)).thenAnswer((_) async => expectedPlan);
|
||||
when(
|
||||
mockChatService.getChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => expectedPlan);
|
||||
|
||||
final result = await mockChatService.getChosenPlan(id: planId);
|
||||
|
||||
@@ -171,31 +288,80 @@ void main() {
|
||||
test('finishChosenPlan - завершает выбранный план', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(mockChatService.finishChosenPlan(id: planId)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.finishChosenPlan(id: planId),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await mockChatService.finishChosenPlan(id: planId);
|
||||
|
||||
verify(mockChatService.finishChosenPlan(id: planId)).called(1);
|
||||
});
|
||||
|
||||
test('complainAboutConsultation - отправляет жалобу на консультацию', () async {
|
||||
const planId = 123;
|
||||
final complainFixture = FixtureReader.readAsMap('chat/plan_complain_response.json');
|
||||
final complainModel = PlanComplainModel.fromJson(complainFixture);
|
||||
test(
|
||||
'complainAboutConsultation - отправляет жалобу на консультацию',
|
||||
() async {
|
||||
const planId = 123;
|
||||
final complainFixture = FixtureReader.readAsMap(
|
||||
'chat/plan_complain_response.json',
|
||||
);
|
||||
final complainModel = PlanComplainModel.fromJson(complainFixture);
|
||||
|
||||
when(mockChatService.complainAboutConsultation(id: planId, body: complainModel)).thenAnswer((_) async => Future.value());
|
||||
when(
|
||||
mockChatService.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
),
|
||||
).thenAnswer((_) async => Future.value());
|
||||
|
||||
await mockChatService.complainAboutConsultation(id: planId, body: complainModel);
|
||||
await mockChatService.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
);
|
||||
|
||||
verify(mockChatService.complainAboutConsultation(id: planId, body: complainModel)).called(1);
|
||||
});
|
||||
verify(
|
||||
mockChatService.complainAboutConsultation(
|
||||
id: planId,
|
||||
body: complainModel,
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'resolveOrderIdToConversation - возвращает модель по orderId',
|
||||
() async {
|
||||
const orderId = 'alfa_c29_k15138_t1712345678_r1234';
|
||||
final fixture = FixtureReader.readAsMap(
|
||||
'chat/payments/order_id_to_chat_uuid_response.json',
|
||||
);
|
||||
final expected = OrderIdToConversationModel.fromJson(fixture);
|
||||
|
||||
when(
|
||||
mockChatService.resolveOrderIdToConversation(orderId: orderId),
|
||||
).thenAnswer((_) async => expected);
|
||||
|
||||
final result = await mockChatService.resolveOrderIdToConversation(
|
||||
orderId: orderId,
|
||||
);
|
||||
|
||||
expect(result, isA<OrderIdToConversationModel>());
|
||||
expect(result.chosenPlanId, 12);
|
||||
verify(
|
||||
mockChatService.resolveOrderIdToConversation(orderId: orderId),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('getChatByUserSlug - возвращает HttpResponse', () async {
|
||||
const slug = 'user-slug';
|
||||
const chatId = 'chat-id-123';
|
||||
final response = HttpResponse({'id': chatId}, Response(requestOptions: RequestOptions(path: ''), statusCode: 200));
|
||||
final response = HttpResponse({
|
||||
'id': chatId,
|
||||
}, Response(requestOptions: RequestOptions(path: ''), statusCode: 200));
|
||||
|
||||
when(mockChatService.getChatByUserSlug(slug: slug)).thenAnswer((_) async => response);
|
||||
when(
|
||||
mockChatService.getChatByUserSlug(slug: slug),
|
||||
).thenAnswer((_) async => response);
|
||||
|
||||
final result = await mockChatService.getChatByUserSlug(slug: slug);
|
||||
|
||||
|
||||
@@ -200,20 +200,20 @@ void main() {
|
||||
.thenAnswer((_) async => chosenPlan);
|
||||
await cubit.getChosenPlan(id: 1);
|
||||
|
||||
when(mockChatRepository.getPaymentLink(id: 1))
|
||||
when(mockChatRepository.getPaymentLink(id: 1, paymentProvider: 'alfa-bank'))
|
||||
.thenAnswer((_) async => paymentModel);
|
||||
|
||||
final result = await cubit.getPaymentLink();
|
||||
|
||||
expect(result, equals('https://payment.url'));
|
||||
verify(mockChatRepository.getPaymentLink(id: 1)).called(1);
|
||||
verify(mockChatRepository.getPaymentLink(id: 1, paymentProvider: 'alfa-bank')).called(1);
|
||||
});
|
||||
|
||||
test('getPaymentLink - возвращает null, если план не выбран', () async {
|
||||
final result = await cubit.getPaymentLink();
|
||||
|
||||
expect(result, isNull);
|
||||
verifyNever(mockChatRepository.getPaymentLink(id: anyNamed('id')));
|
||||
verifyNever(mockChatRepository.getPaymentLink(id: anyNamed('id'), paymentProvider: anyNamed('paymentProvider')));
|
||||
});
|
||||
|
||||
test('getPaymentLink - обработка ошибки', () async {
|
||||
@@ -254,7 +254,7 @@ void main() {
|
||||
.thenAnswer((_) async => chosenPlan);
|
||||
await cubit.getChosenPlan(id: 1);
|
||||
|
||||
when(mockChatRepository.getPaymentLink(id: 1))
|
||||
when(mockChatRepository.getPaymentLink(id: 1, paymentProvider: 'alfa-bank'))
|
||||
.thenThrow(Exception('Ошибка'));
|
||||
|
||||
final result = await cubit.getPaymentLink();
|
||||
|
||||
131
test/features/payment/consultation_payment_flow_test.dart
Normal file
131
test/features/payment/consultation_payment_flow_test.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/payment_flow.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_modal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
Widget createTestApp() {
|
||||
return const MaterialApp(
|
||||
localizationsDelegates: [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [Locale('ru')],
|
||||
home: Scaffold(body: SizedBox()),
|
||||
);
|
||||
}
|
||||
|
||||
group('showConsultationPaymentFlow', () {
|
||||
late bool gateCalled;
|
||||
late bool? gateResult;
|
||||
late bool formCalled;
|
||||
late PromoCodeResult? formResult;
|
||||
late bool sbpCalled;
|
||||
late int sbpPlanId;
|
||||
|
||||
Future<bool?> mockGate(BuildContext context) async {
|
||||
gateCalled = true;
|
||||
return gateResult;
|
||||
}
|
||||
|
||||
Future<PromoCodeResult?> mockForm(
|
||||
BuildContext context, {
|
||||
Future<PromoValidation> Function(String)? onValidate,
|
||||
}) async {
|
||||
formCalled = true;
|
||||
return formResult;
|
||||
}
|
||||
|
||||
Future<void> mockSbp(BuildContext context, {required int planId}) async {
|
||||
sbpCalled = true;
|
||||
sbpPlanId = planId;
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
gateCalled = false;
|
||||
gateResult = null;
|
||||
formCalled = false;
|
||||
formResult = null;
|
||||
sbpCalled = false;
|
||||
sbpPlanId = 0;
|
||||
});
|
||||
|
||||
testWidgets('Нет → вызывает SbpBanks (не форму)', (tester) async {
|
||||
gateResult = false;
|
||||
await tester.pumpWidget(createTestApp());
|
||||
|
||||
await showConsultationPaymentFlow(
|
||||
tester.element(find.byType(SizedBox)),
|
||||
planId: 42,
|
||||
showGate: mockGate,
|
||||
showForm: mockForm,
|
||||
showSbp: mockSbp,
|
||||
);
|
||||
|
||||
expect(gateCalled, isTrue);
|
||||
expect(formCalled, isFalse);
|
||||
expect(sbpCalled, isTrue);
|
||||
expect(sbpPlanId, 42);
|
||||
});
|
||||
|
||||
testWidgets('Да → форма → Пропустить → вызывает SbpBanks', (tester) async {
|
||||
gateResult = true;
|
||||
formResult = const PromoCodeResult();
|
||||
await tester.pumpWidget(createTestApp());
|
||||
|
||||
await showConsultationPaymentFlow(
|
||||
tester.element(find.byType(SizedBox)),
|
||||
planId: 42,
|
||||
showGate: mockGate,
|
||||
showForm: mockForm,
|
||||
showSbp: mockSbp,
|
||||
);
|
||||
|
||||
expect(gateCalled, isTrue);
|
||||
expect(formCalled, isTrue);
|
||||
expect(sbpCalled, isTrue);
|
||||
expect(sbpPlanId, 42);
|
||||
});
|
||||
|
||||
testWidgets('Да → форма отменена (null) → Sbp не вызывается', (tester) async {
|
||||
gateResult = true;
|
||||
formResult = null;
|
||||
await tester.pumpWidget(createTestApp());
|
||||
|
||||
await showConsultationPaymentFlow(
|
||||
tester.element(find.byType(SizedBox)),
|
||||
planId: 42,
|
||||
showGate: mockGate,
|
||||
showForm: mockForm,
|
||||
showSbp: mockSbp,
|
||||
);
|
||||
|
||||
expect(gateCalled, isTrue);
|
||||
expect(formCalled, isTrue);
|
||||
expect(sbpCalled, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('гейт отменён (null) → ничего не вызывается', (tester) async {
|
||||
gateResult = null;
|
||||
await tester.pumpWidget(createTestApp());
|
||||
|
||||
await showConsultationPaymentFlow(
|
||||
tester.element(find.byType(SizedBox)),
|
||||
planId: 42,
|
||||
showGate: mockGate,
|
||||
showForm: mockForm,
|
||||
showSbp: mockSbp,
|
||||
);
|
||||
|
||||
expect(gateCalled, isTrue);
|
||||
expect(formCalled, isFalse);
|
||||
expect(sbpCalled, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/cubit/consultation_payment_cubit/consultation_payment_cubit.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/cubit/consultation_payment_cubit/consultation_payment_state.dart';
|
||||
import 'package:eatvio_app/features/payment/repositories/payment_repository.dart';
|
||||
import 'package:eatvio_app/features/payment/repositories/sbp_repository.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:sbp/models/c2bmembers_model.dart';
|
||||
|
||||
import '../../../../fixtures/fixture_reader.dart';
|
||||
import 'consultation_payment_cubit_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([
|
||||
MockSpec<PaymentRepository>(),
|
||||
MockSpec<SbpRepository>(),
|
||||
])
|
||||
void main() {
|
||||
group('ConsultationPaymentCubit', () {
|
||||
late ConsultationPaymentCubit cubit;
|
||||
late MockPaymentRepository mockPaymentRepository;
|
||||
late MockSbpRepository mockSbpRepository;
|
||||
|
||||
final installedBank = C2bmemberModel.fromJson(const {
|
||||
'bankName': 'Сбербанк',
|
||||
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
|
||||
'schema': 'bank100000000111',
|
||||
'package_name': 'ru.sberbankmobile',
|
||||
'icon': 'packages/sbp/assets/icons/sber.png',
|
||||
});
|
||||
|
||||
late PaymentModel alfaPayment;
|
||||
|
||||
setUp(() {
|
||||
mockPaymentRepository = MockPaymentRepository();
|
||||
mockSbpRepository = MockSbpRepository();
|
||||
cubit = ConsultationPaymentCubit(
|
||||
paymentRepository: mockPaymentRepository,
|
||||
sbpRepository: mockSbpRepository,
|
||||
);
|
||||
alfaPayment = PaymentModel.fromJson(
|
||||
FixtureReader.readAsMap('chat/payments/payment_response_alfa_bank.json'),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
test('начальный state - isLoading=false, banks=[], checkoutUrl=null', () {
|
||||
expect(cubit.state, isA<ConsultationPaymentState>());
|
||||
expect(cubit.state.isLoading, isFalse);
|
||||
expect(cubit.state.banks, isEmpty);
|
||||
expect(cubit.state.checkoutUrl, isNull);
|
||||
expect(cubit.state.orderId, isNull);
|
||||
expect(cubit.state.errorMessage, isNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'startPayment - успешно получает checkout_url и загружает банки',
|
||||
() async {
|
||||
const planId = 123;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(
|
||||
mockSbpRepository.getInstalledBanks(),
|
||||
).thenAnswer((_) async => [installedBank]);
|
||||
|
||||
await cubit.startPayment(planId: planId);
|
||||
|
||||
expect(cubit.state.isLoading, isFalse);
|
||||
expect(cubit.state.checkoutUrl, alfaPayment.paymentUrl);
|
||||
expect(cubit.state.orderId, alfaPayment.orderId);
|
||||
expect(cubit.state.banks, [installedBank]);
|
||||
expect(cubit.state.errorMessage, isNull);
|
||||
verify(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).called(1);
|
||||
verify(mockSbpRepository.getInstalledBanks()).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('startPayment - при ошибке API emit errorMessage', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenThrow(Exception('Payment start failed'));
|
||||
|
||||
await cubit.startPayment(planId: planId);
|
||||
|
||||
expect(cubit.state.isLoading, isFalse);
|
||||
expect(cubit.state.errorMessage, isNotNull);
|
||||
expect(cubit.state.banks, isEmpty);
|
||||
expect(cubit.state.checkoutUrl, isNull);
|
||||
verifyNever(mockSbpRepository.getInstalledBanks());
|
||||
});
|
||||
|
||||
test(
|
||||
'startPayment - при ошибке SBP после успешного payment emit error',
|
||||
() async {
|
||||
const planId = 123;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(
|
||||
mockSbpRepository.getInstalledBanks(),
|
||||
).thenThrow(Exception('SBP plugin error'));
|
||||
|
||||
await cubit.startPayment(planId: planId);
|
||||
|
||||
expect(cubit.state.isLoading, isFalse);
|
||||
expect(cubit.state.errorMessage, isNotNull);
|
||||
expect(cubit.state.checkoutUrl, alfaPayment.paymentUrl);
|
||||
expect(cubit.state.orderId, alfaPayment.orderId);
|
||||
expect(cubit.state.banks, isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
test('openBank - успешно открывает банк после startPayment', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(
|
||||
mockSbpRepository.getInstalledBanks(),
|
||||
).thenAnswer((_) async => [installedBank]);
|
||||
when(
|
||||
mockSbpRepository.openBank(
|
||||
url: alfaPayment.paymentUrl,
|
||||
bank: installedBank,
|
||||
),
|
||||
).thenAnswer((_) async => true);
|
||||
|
||||
await cubit.startPayment(planId: planId);
|
||||
final result = await cubit.openBank(bank: installedBank);
|
||||
|
||||
expect(result, isTrue);
|
||||
verify(
|
||||
mockSbpRepository.openBank(
|
||||
url: alfaPayment.paymentUrl,
|
||||
bank: installedBank,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test(
|
||||
'openBank - без checkoutUrl возвращает false и не вызывает SbpRepository',
|
||||
() async {
|
||||
final result = await cubit.openBank(bank: installedBank);
|
||||
|
||||
expect(result, isFalse);
|
||||
verifyNever(
|
||||
mockSbpRepository.openBank(
|
||||
url: anyNamed('url'),
|
||||
bank: anyNamed('bank'),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('openBank - возвращает false когда банк не открылся', () async {
|
||||
const planId = 123;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(
|
||||
mockSbpRepository.getInstalledBanks(),
|
||||
).thenAnswer((_) async => [installedBank]);
|
||||
when(
|
||||
mockSbpRepository.openBank(
|
||||
url: alfaPayment.paymentUrl,
|
||||
bank: installedBank,
|
||||
),
|
||||
).thenAnswer((_) async => false);
|
||||
|
||||
await cubit.startPayment(planId: planId);
|
||||
final result = await cubit.openBank(bank: installedBank);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_gate_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('PromoCodeGateBottomSheet', () {
|
||||
Widget createTestWidget({required ValueChanged<bool?> onResult}) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Scaffold(
|
||||
body: PromoCodeGateBottomSheet(onResult: onResult),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('показывает заголовок, BottomSheetHandlerBar и кнопки Да/Нет', (tester) async {
|
||||
await tester.pumpWidget(createTestWidget(onResult: (_) {}));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(BottomSheetHandlerBar), findsOneWidget);
|
||||
expect(find.text('У вас есть промокод?'), findsOneWidget);
|
||||
expect(find.text('Да'), findsOneWidget);
|
||||
expect(find.text('Нет'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('tap Да вызывает onResult(true)', (tester) async {
|
||||
bool? result;
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(onResult: (v) => result = v),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('Да'));
|
||||
await tester.pump();
|
||||
|
||||
expect(result, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('tap Нет вызывает onResult(false)', (tester) async {
|
||||
bool? result;
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(onResult: (v) => result = v),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('Нет'));
|
||||
await tester.pump();
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_modal.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('ItvioPromoCodeModal', () {
|
||||
Widget createTestWidget({
|
||||
Future<PromoValidation> Function(String)? onValidate,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Builder(
|
||||
builder: (context) => ElevatedButton(
|
||||
onPressed: () => ItvioPromoCodeModal.show(
|
||||
context,
|
||||
onValidate: onValidate,
|
||||
),
|
||||
child: const Text('Open'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('показывает заголовок Есть промокод? и кнопки Применить/Пропустить', (tester) async {
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.tap(find.text('Open'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Есть промокод?'), findsOneWidget);
|
||||
expect(find.text('Применить'), findsOneWidget);
|
||||
expect(find.text('Пропустить'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Применить с пустым полем показывает ошибку и не закрывает', (tester) async {
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.tap(find.text('Open'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Применить'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Введите промокод'), findsAtLeast(1));
|
||||
expect(find.text('Применить'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Применить с вводом вызывает onValidate и показывает ошибку', (tester) async {
|
||||
PromoValidation? captured;
|
||||
await tester.pumpWidget(
|
||||
createTestWidget(
|
||||
onValidate: (code) async {
|
||||
captured ??= const PromoValidation.failure('Промокод не найден или истёк');
|
||||
return captured!;
|
||||
},
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('Open'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byType(TextField), 'TEST123');
|
||||
await tester.tap(find.text('Применить'));
|
||||
await tester.pump();
|
||||
|
||||
expect(captured, isNotNull);
|
||||
expect(find.text('Промокод не найден или истёк'), findsOneWidget);
|
||||
expect(find.text('Применить'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Пропустить закрывает шторку', (tester) async {
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.tap(find.text('Open'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Пропустить'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Есть промокод?'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('отсутствует UI applied-состояния (Промокод применён)', (tester) async {
|
||||
await tester.pumpWidget(createTestWidget());
|
||||
await tester.tap(find.text('Open'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Промокод применён!'), findsNothing);
|
||||
expect(find.text('Продолжить'), findsNothing);
|
||||
expect(find.text('Убрать промокод'), findsNothing);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/cubit/consultation_payment_cubit/consultation_payment_cubit.dart';
|
||||
import 'package:eatvio_app/features/payment/presentation/widgets/sbp_banks_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/features/payment/repositories/payment_repository.dart';
|
||||
import 'package:eatvio_app/features/payment/repositories/sbp_repository.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:sbp/models/c2bmembers_model.dart';
|
||||
|
||||
import '../../../../fixtures/fixture_reader.dart';
|
||||
import 'sbp_banks_bottom_sheet_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([
|
||||
MockSpec<PaymentRepository>(),
|
||||
MockSpec<SbpRepository>(),
|
||||
])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('SbpBanksBottomSheet', () {
|
||||
late MockPaymentRepository mockPaymentRepository;
|
||||
late MockSbpRepository mockSbpRepository;
|
||||
late ConsultationPaymentCubit cubit;
|
||||
late PaymentModel alfaPayment;
|
||||
|
||||
final installedBank = C2bmemberModel.fromJson(const {
|
||||
'bankName': 'Сбербанк',
|
||||
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
|
||||
'schema': 'bank100000000111',
|
||||
'package_name': 'ru.sberbankmobile',
|
||||
'icon': '',
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockPaymentRepository = MockPaymentRepository();
|
||||
mockSbpRepository = MockSbpRepository();
|
||||
cubit = ConsultationPaymentCubit(
|
||||
paymentRepository: mockPaymentRepository,
|
||||
sbpRepository: mockSbpRepository,
|
||||
);
|
||||
alfaPayment = PaymentModel.fromJson(
|
||||
FixtureReader.readAsMap('chat/payments/payment_response_alfa_bank.json'),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
Widget createTestWidget({required int planId}) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Scaffold(
|
||||
body: SbpBanksBottomSheet(
|
||||
planId: planId,
|
||||
cubit: cubit,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('показывает Loader пока идёт startPayment', (tester) async {
|
||||
const planId = 12;
|
||||
final completer = Completer<PaymentModel>();
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) => completer.future);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(Loader), findsOneWidget);
|
||||
expect(find.text('Сбербанк'), findsNothing);
|
||||
|
||||
completer.complete(alfaPayment);
|
||||
when(mockSbpRepository.getInstalledBanks()).thenAnswer((_) async => [installedBank]);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('вызывает startPaymentProcess с переданным planId', (tester) async {
|
||||
const planId = 12;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(mockSbpRepository.getInstalledBanks()).thenAnswer((_) async => [installedBank]);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(mockPaymentRepository.startPaymentProcess(planId: planId)).called(1);
|
||||
});
|
||||
|
||||
testWidgets('после успеха показывает список банков и скрывает Loader', (tester) async {
|
||||
const planId = 12;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(mockSbpRepository.getInstalledBanks()).thenAnswer((_) async => [installedBank]);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Loader), findsNothing);
|
||||
expect(find.text('Выберите банк'), findsOneWidget);
|
||||
expect(find.text('Сбербанк'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('tap по банку вызывает openBank с checkout_url', (tester) async {
|
||||
const planId = 12;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(mockSbpRepository.getInstalledBanks()).thenAnswer((_) async => [installedBank]);
|
||||
when(
|
||||
mockSbpRepository.openBank(
|
||||
url: alfaPayment.paymentUrl,
|
||||
bank: installedBank,
|
||||
),
|
||||
).thenAnswer((_) async => true);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Сбербанк'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
verify(
|
||||
mockSbpRepository.openBank(
|
||||
url: alfaPayment.paymentUrl,
|
||||
bank: installedBank,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
testWidgets('при ошибке API показывает сообщение об ошибке', (tester) async {
|
||||
const planId = 12;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenThrow(Exception('Payment start failed'));
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Loader), findsNothing);
|
||||
expect(find.text('Сбербанк'), findsNothing);
|
||||
expect(
|
||||
find.text('Не удалось начать оплату. Попробуйте позже'),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('при пустом списке банков показывает empty-сообщение', (tester) async {
|
||||
const planId = 12;
|
||||
|
||||
when(
|
||||
mockPaymentRepository.startPaymentProcess(planId: planId),
|
||||
).thenAnswer((_) async => alfaPayment);
|
||||
when(mockSbpRepository.getInstalledBanks()).thenAnswer((_) async => []);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(planId: planId));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Loader), findsNothing);
|
||||
expect(find.text('Нет установленных банков'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import 'package:eatvio_app/features/chat/models/payment_model.dart';
|
||||
import 'package:eatvio_app/features/chat/services/chat_service.dart';
|
||||
import 'package:eatvio_app/features/payment/repositories/payment_repository.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import '../../../fixtures/fixture_reader.dart';
|
||||
import 'payment_repository_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<ChatService>()])
|
||||
void main() {
|
||||
group('PaymentRepository', () {
|
||||
late PaymentRepository paymentRepository;
|
||||
late MockChatService mockChatService;
|
||||
|
||||
setUp(() {
|
||||
mockChatService = MockChatService();
|
||||
paymentRepository = PaymentRepository(chatService: mockChatService);
|
||||
});
|
||||
|
||||
test(
|
||||
'startPaymentProcess - возвращает PaymentModel из ответа alfa-bank',
|
||||
() async {
|
||||
const planId = 123;
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response_alfa_bank.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
final requestBody = Map<String, String>.from(
|
||||
FixtureReader.readAsMap('chat/payments/payment_alfabank_request.json'),
|
||||
);
|
||||
|
||||
when(
|
||||
mockChatService.getPaymentLink(id: planId, body: requestBody),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
final result = await paymentRepository.startPaymentProcess(planId: planId);
|
||||
|
||||
expect(result, isA<PaymentModel>());
|
||||
expect(result.paymentUrl, 'https://qr.nspk.ru/ASDFASDFASDFASDF');
|
||||
expect(result.orderId, 'alfa_c29_k15138_t1712345678_r1234');
|
||||
expect(result.paymentProvider, 'alfa-bank');
|
||||
verify(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'startPaymentProcess - передаёт payment_provider alfa-bank в тело запроса',
|
||||
() async {
|
||||
const planId = 42;
|
||||
final paymentFixture = FixtureReader.readAsMap(
|
||||
'chat/payments/payment_response_alfa_bank.json',
|
||||
);
|
||||
final expectedPayment = PaymentModel.fromJson(paymentFixture);
|
||||
|
||||
when(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).thenAnswer((_) async => expectedPayment);
|
||||
|
||||
await paymentRepository.startPaymentProcess(planId: planId);
|
||||
|
||||
verify(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('startPaymentProcess - пробрасывает ошибку сервиса', () async {
|
||||
const planId = 123;
|
||||
final error = Exception('Payment start failed');
|
||||
|
||||
when(
|
||||
mockChatService.getPaymentLink(
|
||||
id: planId,
|
||||
body: {'payment_provider': 'alfa-bank'},
|
||||
),
|
||||
).thenThrow(error);
|
||||
|
||||
expect(
|
||||
() => paymentRepository.startPaymentProcess(planId: planId),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
118
test/features/payment/repositories/sbp_repository_test.dart
Normal file
118
test/features/payment/repositories/sbp_repository_test.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'package:eatvio_app/features/payment/repositories/sbp_repository.dart';
|
||||
import 'package:eatvio_app/features/payment/services/sbp_service.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:sbp/models/c2bmembers_model.dart';
|
||||
|
||||
import 'sbp_repository_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<SbpService>()])
|
||||
void main() {
|
||||
group('SbpRepository', () {
|
||||
late SbpRepository sbpRepository;
|
||||
late MockSbpService mockSbpService;
|
||||
|
||||
final installedBank = C2bmemberModel.fromJson(const {
|
||||
'bankName': 'Сбербанк',
|
||||
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
|
||||
'schema': 'bank100000000111',
|
||||
'package_name': 'ru.sberbankmobile',
|
||||
'icon': 'packages/sbp/assets/icons/sber.png',
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockSbpService = MockSbpService();
|
||||
sbpRepository = SbpRepository(sbpService: mockSbpService);
|
||||
});
|
||||
|
||||
group('getInstalledBanks', () {
|
||||
test(
|
||||
'делегирует сервису и возвращает список установленных банков',
|
||||
() async {
|
||||
when(
|
||||
mockSbpService.getInstalledBanks(),
|
||||
).thenAnswer((_) async => [installedBank]);
|
||||
|
||||
final result = await sbpRepository.getInstalledBanks();
|
||||
|
||||
expect(result, [installedBank]);
|
||||
verify(mockSbpService.getInstalledBanks()).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'возвращает пустой список, когда сервис вернул пустой список',
|
||||
() async {
|
||||
when(mockSbpService.getInstalledBanks()).thenAnswer((_) async => []);
|
||||
|
||||
final result = await sbpRepository.getInstalledBanks();
|
||||
|
||||
expect(result, isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'бросает exception при ошибке нативного плагина (graceful)',
|
||||
() async {
|
||||
when(
|
||||
mockSbpService.getInstalledBanks(),
|
||||
).thenThrow(MissingPluginException('no sbp handler'));
|
||||
|
||||
expect(() => sbpRepository.getInstalledBanks(), throwsException);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('openBank', () {
|
||||
const checkoutUrl = 'https://qr.nspk.ru/ASDFASDFASDFASDF';
|
||||
|
||||
test(
|
||||
'делегирует сервису и возвращает true при успешном запуске',
|
||||
() async {
|
||||
when(
|
||||
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
|
||||
).thenAnswer((_) async => true);
|
||||
|
||||
final result = await sbpRepository.openBank(
|
||||
url: checkoutUrl,
|
||||
bank: installedBank,
|
||||
);
|
||||
|
||||
expect(result, isTrue);
|
||||
verify(
|
||||
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test('возвращает false, когда сервис вернул false', () async {
|
||||
when(
|
||||
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
|
||||
).thenAnswer((_) async => false);
|
||||
|
||||
final result = await sbpRepository.openBank(
|
||||
url: checkoutUrl,
|
||||
bank: installedBank,
|
||||
);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test(
|
||||
'возвращает false при ошибке нативного плагина (graceful)',
|
||||
() async {
|
||||
when(
|
||||
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
|
||||
).thenThrow(MissingPluginException('no sbp handler'));
|
||||
|
||||
expect(
|
||||
() => sbpRepository.openBank(url: checkoutUrl, bank: installedBank),
|
||||
throwsException,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
119
test/features/payment/services/sbp_service_test.dart
Normal file
119
test/features/payment/services/sbp_service_test.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:eatvio_app/features/payment/services/sbp_service.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sbp/models/c2bmembers_model.dart';
|
||||
|
||||
/// Тесты реализации [SbpServiceImpl] — обёртки над статичным классом [Sbp].
|
||||
///
|
||||
/// Поскольку unit-тесты исполняются на хост-машине (не iOS), срабатывает
|
||||
/// Android-ветка плагина: `getInstalledBanks` возвращает список банков через
|
||||
/// `package_name`, а `openBank` подставляет схему банка в начало checkout_url.
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
const channel = MethodChannel('sbp');
|
||||
|
||||
/// Банк-фикстура: Сбербанк (присутствует в справочнике c2bmembersData пакета).
|
||||
final sberbank = C2bmemberModel.fromJson(const {
|
||||
'bankName': 'Сбербанк',
|
||||
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
|
||||
'schema': 'bank100000000111',
|
||||
'package_name': 'ru.sberbankmobile',
|
||||
'icon': 'packages/sbp/assets/icons/sber.png',
|
||||
});
|
||||
|
||||
late SbpService sbpService;
|
||||
|
||||
setUp(() {
|
||||
sbpService = const SbpServiceImpl();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
group('SbpServiceImpl.getInstalledBanks', () {
|
||||
test('возвращает список банков, установленных на устройстве (Android)', () async {
|
||||
// Плагин (Android) возвращает список установленных банков в формате
|
||||
// {name, bitmap, package_name}; обёртка сопоставляет со справочником.
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (MethodCall call) async {
|
||||
return [
|
||||
{'name': 'Сбербанк', 'bitmap': null, 'package_name': 'ru.sberbankmobile'},
|
||||
];
|
||||
});
|
||||
|
||||
final result = await sbpService.getInstalledBanks();
|
||||
|
||||
expect(result, isNotEmpty);
|
||||
expect(
|
||||
result.any(
|
||||
(bank) =>
|
||||
bank.bankName == 'Сбербанк' && bank.packageName == 'ru.sberbankmobile',
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('возвращает пустой список, если нет установленных банков', () async {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (MethodCall call) async {
|
||||
return <Map<String, dynamic>>[];
|
||||
});
|
||||
|
||||
final result = await sbpService.getInstalledBanks();
|
||||
|
||||
expect(result, isEmpty);
|
||||
});
|
||||
|
||||
test('пробрасывает ошибку плагина при отсутствии нативного обработчика', () async {
|
||||
// Обработчик не зарегистрирован → invokeMethod выбрасывает
|
||||
// MissingPluginException. Сервис должен пробросить её наверх.
|
||||
expect(
|
||||
sbpService.getInstalledBanks(),
|
||||
throwsA(isA<MissingPluginException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('SbpServiceImpl.openBank', () {
|
||||
test('вызывает нативный openBank с подстановкой схемы банка в url и возвращает true', () async {
|
||||
const checkoutUrl = 'https://qr.nspk.ru/ASDFASDFASDFASDF';
|
||||
String? receivedUrl;
|
||||
String? receivedPackageName;
|
||||
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (MethodCall call) async {
|
||||
if (call.method == 'openBank') {
|
||||
final args = call.arguments as Map<dynamic, dynamic>;
|
||||
receivedUrl = args['url'] as String?;
|
||||
receivedPackageName = args['package_name'] as String?;
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
final result = await sbpService.openBank(url: checkoutUrl, bank: sberbank);
|
||||
|
||||
expect(result, isTrue);
|
||||
// Android-ветка заменяет схему "https" на schema банка в начале url.
|
||||
expect(receivedUrl, 'bank100000000111://qr.nspk.ru/ASDFASDFASDFASDF');
|
||||
expect(receivedPackageName, 'ru.sberbankmobile');
|
||||
});
|
||||
|
||||
test('возвращает false при неудачном запуске банковского приложения', () async {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (MethodCall call) async {
|
||||
return false;
|
||||
});
|
||||
|
||||
final result = await sbpService.openBank(
|
||||
url: 'https://qr.nspk.ru/ASDFASDFASDFASDF',
|
||||
bank: sberbank,
|
||||
);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
void main() {
|
||||
group('ChosenPlanStateEnum', () {
|
||||
test('has all expected values', () {
|
||||
expect(ChosenPlanStateEnum.values.length, 15);
|
||||
expect(ChosenPlanStateEnum.values.length, 16);
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.stateInited));
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.stateStartDateHasBeenSet));
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.stateInCoordination));
|
||||
@@ -20,6 +20,7 @@ void main() {
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.stateDone));
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.personalDietSet));
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.stateChargeWasCollected));
|
||||
expect(ChosenPlanStateEnum.values, contains(ChosenPlanStateEnum.statePaymentFailed));
|
||||
});
|
||||
|
||||
test('getShowableChatBadgeStates returns correct states', () {
|
||||
@@ -35,6 +36,7 @@ void main() {
|
||||
expect(states, contains(ChosenPlanStateEnum.stateDone));
|
||||
expect(states, contains(ChosenPlanStateEnum.stateInProgress));
|
||||
expect(states, contains(ChosenPlanStateEnum.personalDietSet));
|
||||
expect(states, isNot(contains(ChosenPlanStateEnum.statePaymentFailed)));
|
||||
});
|
||||
|
||||
test('isStateAfterPaidState returns false for states before paid', () {
|
||||
@@ -43,6 +45,7 @@ void main() {
|
||||
expect(ChosenPlanStateEnum.stateInCoordination.isStateAfterPaidState(), isFalse);
|
||||
expect(ChosenPlanStateEnum.stateStartDateHasBeenSet.isStateAfterPaidState(), isFalse);
|
||||
expect(ChosenPlanStateEnum.statePendingPayment.isStateAfterPaidState(), isFalse);
|
||||
expect(ChosenPlanStateEnum.statePaymentFailed.isStateAfterPaidState(), isFalse);
|
||||
});
|
||||
|
||||
test('isStateAfterPaidState returns true for states after paid', () {
|
||||
@@ -70,6 +73,7 @@ void main() {
|
||||
expect(activeStates, contains(ChosenPlanStateEnum.stateComplained));
|
||||
expect(activeStates, contains(ChosenPlanStateEnum.stateDone));
|
||||
expect(activeStates, contains(ChosenPlanStateEnum.personalDietSet));
|
||||
expect(activeStates, isNot(contains(ChosenPlanStateEnum.statePaymentFailed)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ 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 '../../../fixtures/fixture_reader.dart';
|
||||
|
||||
void main() {
|
||||
group('ChosenPlanModel', () {
|
||||
test('создает ChosenPlanModel с правильными значениями', () {
|
||||
@@ -140,6 +142,60 @@ void main() {
|
||||
expect(json['consultant_id'], 1);
|
||||
expect(json.containsKey('plan'), isTrue);
|
||||
});
|
||||
|
||||
group('fromJson payment polling statuses', () {
|
||||
test('fromJson парсит pending_payment из фикстуры', () {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_pending_payment_response.json',
|
||||
);
|
||||
|
||||
final model = ChosenPlanModel.fromJson(json);
|
||||
|
||||
expect(model.id, 1);
|
||||
expect(model.optionType, '1');
|
||||
expect(model.chosenPlanState, ChosenPlanStateEnum.statePendingPayment);
|
||||
expect(model.chatId, 'chat-1');
|
||||
});
|
||||
|
||||
test('fromJson парсит paid из фикстуры', () {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_paid_response.json',
|
||||
);
|
||||
|
||||
final model = ChosenPlanModel.fromJson(json);
|
||||
|
||||
expect(model.id, 1);
|
||||
expect(model.optionType, '1');
|
||||
expect(model.chosenPlanState, ChosenPlanStateEnum.statePaid);
|
||||
expect(model.chatId, 'chat-1');
|
||||
});
|
||||
|
||||
test('fromJson парсит payment_failed из фикстуры', () {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_payment_failed_response.json',
|
||||
);
|
||||
|
||||
final model = ChosenPlanModel.fromJson(json);
|
||||
|
||||
expect(model.id, 1);
|
||||
expect(model.optionType, '1');
|
||||
expect(model.chosenPlanState, ChosenPlanStateEnum.statePaymentFailed);
|
||||
expect(model.chatId, 'chat-1');
|
||||
});
|
||||
|
||||
test('fromJson парсит canceled_by_the_initiator из фикстуры', () {
|
||||
final json = FixtureReader.readAsMap(
|
||||
'chat/chosen_plan/chosen_plan_canceled_by_the_initiator_response.json',
|
||||
);
|
||||
|
||||
final model = ChosenPlanModel.fromJson(json);
|
||||
|
||||
expect(model.id, 1);
|
||||
expect(model.optionType, '1');
|
||||
expect(model.chosenPlanState, ChosenPlanStateEnum.stateCanceledByTheInitiator);
|
||||
expect(model.chatId, 'chat-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('parseChosenPlan', () {
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_canceled_by_the_initiator_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_canceled_by_the_initiator_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "canceled_by_the_initiator",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_charge_was_callected_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_charge_was_callected_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "charge_was_collected",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_complained_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_complained_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "complained",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_confirmed_by_consultant_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_confirmed_by_consultant_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "confirmed_by_consultant",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_done_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_done_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "done",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_in_progress_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_in_progress_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "in_progress",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_initied_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_initied_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "initied",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_last_day_finished_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_last_day_finished_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "last_day_finished",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_last_day_started_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_last_day_started_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "last_day_started",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_paid_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_paid_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "paid",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_payment_failed_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_payment_failed_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "payment_failed",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_pending_payment_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_pending_payment_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "pending_payment",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_presonal_diet_set_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_presonal_diet_set_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "personal_diet_setted",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_start_date_has_been_set_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_start_date_has_been_set_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "start_date_has_been_set",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
47
test/fixtures/chat/chosen_plan/chosen_plan_started_response.json
vendored
Normal file
47
test/fixtures/chat/chosen_plan/chosen_plan_started_response.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"id": 1,
|
||||
"option_type": "1",
|
||||
"start_date": "2024-01-01T00:00:00Z",
|
||||
"last_state": "started",
|
||||
"chat_id": "chat-1",
|
||||
"consultant_id": 1,
|
||||
"plan": {
|
||||
"id": 1,
|
||||
"slug": "test-plan",
|
||||
"name": "Test Plan",
|
||||
"image": "https://example.com/plan.jpg",
|
||||
"duration": 30,
|
||||
"state": "active",
|
||||
"type": {
|
||||
"fit": true,
|
||||
"slim": false,
|
||||
"behavior": false,
|
||||
"wellness": false
|
||||
},
|
||||
"description_md": "Plan description",
|
||||
"packets_count": 1,
|
||||
"packet1_options": {
|
||||
"items": ["Item 1", "Item 2"],
|
||||
"price": 1500.0,
|
||||
"short": "Option 1",
|
||||
"description_md": "Description",
|
||||
"has_personal_diet": false
|
||||
},
|
||||
"packet2_options": null,
|
||||
"packet3_options": null,
|
||||
"author": {
|
||||
"author_slug": "author-1",
|
||||
"author_name": "Author Name",
|
||||
"author_profile_icon": "https://example.com/author.jpg",
|
||||
"author_is_me": false
|
||||
},
|
||||
"total_likes": 10,
|
||||
"total_views": 100,
|
||||
"total_comments": 5,
|
||||
"is_favorite": false,
|
||||
"min_price_of_options": 1000.0,
|
||||
"max_price_of_options": 2000.0,
|
||||
"was_liked_by_me": false
|
||||
}
|
||||
}
|
||||
|
||||
5
test/fixtures/chat/payments/order_id_to_chat_uuid_response.json
vendored
Normal file
5
test/fixtures/chat/payments/order_id_to_chat_uuid_response.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"chosen_plan_id": 12,
|
||||
"conversation_uuid": "fbd81429-8019-499a-80e8-e3933cc4d872"
|
||||
}
|
||||
3
test/fixtures/chat/payments/payment_alfabank_request.json
vendored
Normal file
3
test/fixtures/chat/payments/payment_alfabank_request.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"payment_provider": "alfa-bank"
|
||||
}
|
||||
3
test/fixtures/chat/payments/payment_mock_requeste.json
vendored
Normal file
3
test/fixtures/chat/payments/payment_mock_requeste.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"payment_provider": "mock"
|
||||
}
|
||||
5
test/fixtures/chat/payments/payment_response_alfa_bank.json
vendored
Normal file
5
test/fixtures/chat/payments/payment_response_alfa_bank.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"checkout_url": "https://qr.nspk.ru/ASDFASDFASDFASDF",
|
||||
"payment_provider": "alfa-bank",
|
||||
"order_id": "alfa_c29_k15138_t1712345678_r1234"
|
||||
}
|
||||
Reference in New Issue
Block a user