148 lines
4.7 KiB
Dart
148 lines
4.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter_app/core/network/api_repository.dart';
|
|
import 'package:flutter_app/features/chat/presentation/cubit/chat_cubit.dart';
|
|
import 'package:flutter_app/features/chat/presentation/cubit/chat_state.dart';
|
|
|
|
class _VersionTestApi extends ApiRepository {
|
|
_VersionTestApi() : super(baseUrl: 'http://127.0.0.1:8000');
|
|
|
|
final responses = <List<String>>[
|
|
['الجواب الأول'],
|
|
['الجواب الثاني'],
|
|
];
|
|
SavedConversation? lastSaved;
|
|
|
|
@override
|
|
Future<String> getModelName() async => 'gemma4:e2b';
|
|
|
|
@override
|
|
Future<List<String>> getAvailableModels() async => ['gemma4:e2b'];
|
|
|
|
@override
|
|
Future<List<SavedConversation>> listConversations() async => [];
|
|
|
|
@override
|
|
Future<void> saveConversation(SavedConversation conversation) async {
|
|
lastSaved = conversation;
|
|
}
|
|
|
|
@override
|
|
Stream<String> completeStream(List<ChatMessage> messages, {String? model}) =>
|
|
Stream<String>.fromIterable(responses.removeAt(0));
|
|
}
|
|
|
|
class _CancellableTestApi extends _VersionTestApi {
|
|
final controller = StreamController<String>();
|
|
var cancelled = false;
|
|
final storedConversation = SavedConversation(
|
|
id: 'conversation',
|
|
title: 'سؤال قديم',
|
|
updatedAt: DateTime.utc(2026),
|
|
messages: const [
|
|
ChatMessage('سؤال قديم', fromUser: true),
|
|
ChatMessage(
|
|
'إجابة محفوظة',
|
|
fromUser: false,
|
|
answerVersions: ['إجابة محفوظة'],
|
|
),
|
|
],
|
|
);
|
|
|
|
@override
|
|
Future<SavedConversation> getConversation(String id) async =>
|
|
storedConversation;
|
|
|
|
@override
|
|
Stream<String> completeStream(List<ChatMessage> messages, {String? model}) =>
|
|
controller.stream;
|
|
|
|
@override
|
|
void cancelActiveRequest() {
|
|
cancelled = true;
|
|
controller.close();
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
test('regeneration keeps prior answers and selection is persisted', () async {
|
|
final api = _VersionTestApi();
|
|
final cubit = ChatCubit(api);
|
|
await Future<void>.delayed(Duration.zero);
|
|
|
|
await cubit.send('ما معنى API؟');
|
|
expect(cubit.state.messages.last.answerVersions, ['الجواب الأول']);
|
|
expect(cubit.state.modelStatus, ModelConnectionStatus.connected);
|
|
expect(cubit.state.requestStatus, RequestStatus.completed);
|
|
expect(cubit.state.lastResponseDuration, isNotNull);
|
|
|
|
await cubit.regenerateLastAnswer();
|
|
expect(cubit.state.messages.last.text, 'الجواب الثاني');
|
|
expect(cubit.state.messages.last.answerVersions, [
|
|
'الجواب الأول',
|
|
'الجواب الثاني',
|
|
]);
|
|
expect(cubit.state.messages.last.selectedVersion, 1);
|
|
|
|
await cubit.selectAnswerVersion(1, 0);
|
|
expect(cubit.state.messages.last.text, 'الجواب الأول');
|
|
expect(cubit.state.messages.last.selectedVersion, 0);
|
|
expect(api.lastSaved?.messages.last.answerVersions, [
|
|
'الجواب الأول',
|
|
'الجواب الثاني',
|
|
]);
|
|
expect(api.lastSaved?.messages.last.selectedVersion, 0);
|
|
|
|
await cubit.close();
|
|
});
|
|
|
|
test(
|
|
'cancelling generation preserves partial text and records duration',
|
|
() async {
|
|
final api = _CancellableTestApi();
|
|
final cubit = ChatCubit(api);
|
|
await Future<void>.delayed(Duration.zero);
|
|
|
|
final send = cubit.send('اكتب فقرة قصيرة');
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
api.controller.add('بداية الإجابة');
|
|
await Future<void>.delayed(Duration.zero);
|
|
await cubit.cancelGeneration();
|
|
await send;
|
|
|
|
expect(api.cancelled, isTrue);
|
|
expect(cubit.state.isSending, isFalse);
|
|
expect(cubit.state.requestStatus, RequestStatus.cancelled);
|
|
expect(cubit.state.messages.last.text, 'بداية الإجابة');
|
|
expect(cubit.state.lastResponseDuration, isNotNull);
|
|
expect(api.lastSaved?.messages.last.text, 'بداية الإجابة');
|
|
|
|
await cubit.close();
|
|
await api.controller.close();
|
|
},
|
|
);
|
|
|
|
test(
|
|
'cancelling regeneration before its first token keeps saved answer',
|
|
() async {
|
|
final api = _CancellableTestApi();
|
|
final cubit = ChatCubit(api);
|
|
await Future<void>.delayed(Duration.zero);
|
|
await cubit.openConversation(api.storedConversation);
|
|
|
|
final regeneration = cubit.regenerateLastAnswer();
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
await cubit.cancelGeneration();
|
|
await regeneration;
|
|
|
|
expect(cubit.state.messages.last.text, 'إجابة محفوظة');
|
|
expect(api.lastSaved?.messages.last.text, 'إجابة محفوظة');
|
|
expect(cubit.state.requestStatus, RequestStatus.cancelled);
|
|
|
|
await cubit.close();
|
|
await api.controller.close();
|
|
},
|
|
);
|
|
}
|