import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:ikar/backend/modules/messages.dart'; import 'package:ikar/core/media/voice_transcription.dart'; import 'package:ikar/models/attachment.dart'; CachedMessage _message({ String? status = 'sent', bool deleted = false, List? attachments, }) => CachedMessage( id: '101', accountId: 1, chatId: 10, senderId: 7, time: 1, status: status, deleted: deleted, attachments: attachments ?? const [AudioAttachment(audioId: 42)], ); void main() { setUp(TranscriptionCache.clear); tearDown(TranscriptionCache.clear); test('voice target retains the original IDs of a forwarded recording', () { final target = VoiceTranscriptionTarget.fromMessage( _message( attachments: const [ ForwardedMessageAttachment( originalSenderId: 9, originalChatId: 20, originalMessageId: '202', originalAttachments: [AudioAttachment(audioId: 43)], ), ], ), )!; expect((target.chatId, target.messageId, target.audioId), (20, '202', 43)); }); test( 'text, videos, files and unsent voices have no transcription action', () { for (final attachments in >[ [], [const VideoAttachment(videoId: 42)], [const FileAttachment(fileId: 42, name: 'audio.mp3')], [const AudioAttachment()], ]) { expect( VoiceTranscriptionTarget.fromMessage( _message(attachments: attachments), ), isNull, ); } for (final status in ['sending', 'pending', 'error']) { expect( VoiceTranscriptionTarget.fromMessage(_message(status: status)), isNull, ); } expect( VoiceTranscriptionTarget.fromMessage(_message(deleted: true)), isNull, ); }, ); test( 'request shows progress, deduplicates taps and displays result', () async { final target = VoiceTranscriptionTarget.fromMessage(_message())!; final response = Completer(); var requests = 0; Future request(int chat, int message, int audio) { expect((chat, message, audio), (10, 101, 42)); requests++; return response.future; } final first = VoiceTranscription.toggle(target, request); expect(TranscriptionCache.get('101')?.status, 0); expect(TranscriptionCache.isExpanded('101'), isTrue); await VoiceTranscription.toggle(target, request); expect(requests, 1); response.complete(TranscriptionResult(status: 1, text: 'Привет')); await first; expect(TranscriptionCache.get('101')?.text, 'Привет'); }, ); test( 'completed text hides and reopens without a new server request', () async { final target = VoiceTranscriptionTarget.fromMessage(_message())!; TranscriptionCache.put( '101', TranscriptionResult(status: 1, text: 'Привет'), expanded: true, ); var changes = 0; void listener() => changes++; TranscriptionCache.listen('101', listener); addTearDown(() => TranscriptionCache.unlisten('101', listener)); Future noRequest( int chat, int message, int audio, ) async { fail('Cached transcription must not request the server'); } await VoiceTranscription.toggle(target, noRequest); expect(TranscriptionCache.isExpanded('101'), isFalse); await VoiceTranscription.toggle(target, noRequest); expect(TranscriptionCache.isExpanded('101'), isTrue); expect(changes, 2); }, ); test('failed recognition can be retried', () async { final target = VoiceTranscriptionTarget.fromMessage(_message())!; await VoiceTranscription.toggle( target, (_, _, _) async => throw StateError('offline'), ); expect(TranscriptionCache.get('101')?.status, -1); await VoiceTranscription.toggle( target, (_, _, _) async => TranscriptionResult(status: 1, text: 'Повтор'), ); expect(TranscriptionCache.get('101')?.text, 'Повтор'); }); test('a queued response cannot replace the completed push result', () async { final target = VoiceTranscriptionTarget.fromMessage(_message())!; final response = Completer(); final pending = VoiceTranscription.toggle( target, (_, _, _) => response.future, ); TranscriptionCache.put( '101', TranscriptionResult(status: 1, text: 'Готово'), expanded: true, ); response.complete(TranscriptionResult(status: 0)); await pending; expect(TranscriptionCache.get('101')?.text, 'Готово'); expect(TranscriptionCache.get('101')?.status, 1); }); test('a late request error cannot erase a completed push result', () async { final target = VoiceTranscriptionTarget.fromMessage(_message())!; final response = Completer(); final pending = VoiceTranscription.toggle( target, (_, _, _) => response.future, ); TranscriptionCache.put( '101', TranscriptionResult(status: 1, text: 'Готово'), expanded: true, ); response.completeError(StateError('connection lost after push')); await pending; expect(TranscriptionCache.get('101')?.text, 'Готово'); expect(TranscriptionCache.get('101')?.status, 1); }); }