import 'package:eaimar/modules/translation/models/translation_export.dart'; import 'package:eaimar/modules/translation/models/translation_models.dart'; import 'package:flutter_test/flutter_test.dart'; TranslationItem _item( DateTime at, { String src = '你好', String dst = 'Hello', String srcLang = 'zh-CN', String serviceId = '', bool intermediate = false, }) => TranslationItem( sourceText: src, translatedText: dst, sourceLanguageCode: srcLang, targetLanguageCode: srcLang == 'zh-CN' ? 'en-US' : 'zh-CN', timestamp: at, sessionId: 's', serviceId: serviceId, isIntermediate: intermediate, ); void main() { final t0 = DateTime(2026, 9, 18, 10, 0, 0); test('只取录音开始之后的整句,时间是相对录音起点的毫秒', () { final out = buildMeetingTranscript( items: [ _item(t0.subtract(const Duration(seconds: 5)), src: '上一轮的'), _item(t0.add(const Duration(seconds: 2))), _item(t0.add(const Duration(seconds: 6)), src: '半句', intermediate: true), _item(t0.add(const Duration(seconds: 9)), src: '第二句', dst: 'Second'), ], recordingStartedAt: t0, mode: 'simultaneous', ownLanguageCode: 'zh-CN', ); expect(out.length, 2); expect(out[0]['starttime'], 2000); expect(out[0]['endtime'], 9000); // 下一句开头 expect(out[0]['content'], '你好\nHello'); expect(out[0]['speaker'], ''); // 同传单向,不标说话人 expect(out[1]['starttime'], 9000); expect(out[1]['endtime'], greaterThan(9000)); // 最后一句按字数估 }); test('通话翻译按 AST 的 serviceId 分说话人:A=0 本端 B=1 对端', () { final out = buildMeetingTranscript( items: [ _item(t0.add(const Duration(seconds: 1)), serviceId: 'A'), _item(t0.add(const Duration(seconds: 3)), src: 'Hi', dst: '嗨', srcLang: 'en-US', serviceId: 'B'), ], recordingStartedAt: t0, mode: 'call', ownLanguageCode: 'zh-CN', ); expect(out.map((e) => e['speaker']), ['Speaker_0', 'Speaker_1']); }); test('面对面没有 serviceId,按这句话的语言分:说源语言的是本端', () { final out = buildMeetingTranscript( items: [ _item(t0.add(const Duration(seconds: 1))), _item(t0.add(const Duration(seconds: 3)), src: 'Hi', dst: '嗨', srcLang: 'en-US'), ], recordingStartedAt: t0, mode: 'faceToFace', ownLanguageCode: 'zh-CN', ); expect(out.map((e) => e['speaker']), ['Speaker_0', 'Speaker_1']); }); test('暂停区间要从偏移里扣掉(面对面逐句松手即暂停)', () { final out = buildMeetingTranscript( items: [ _item(t0.add(const Duration(seconds: 2))), // 3s~13s 暂停了 10 秒,这句在 15s 说的,音频里其实是第 5 秒 _item(t0.add(const Duration(seconds: 15)), src: '第二句', dst: ''), ], recordingStartedAt: t0, mode: 'faceToFace', ownLanguageCode: 'zh-CN', pauses: [ (t0.add(const Duration(seconds: 3)), t0.add(const Duration(seconds: 13))) ], ); expect(out[0]['starttime'], 2000); expect(out[0]['endtime'], 5000); expect(out[1]['starttime'], 5000); expect(out[1]['content'], '第二句'); // 没译文就只有原文一行 }); test('译文与原文相同时不重复', () { final out = buildMeetingTranscript( items: [_item(t0.add(const Duration(seconds: 1)), src: 'OK', dst: 'OK')], recordingStartedAt: t0, mode: 'simultaneous', ownLanguageCode: 'zh-CN', ); expect(out.single['content'], 'OK'); }); test('没有整句就返回空,调用方据此退回普通录音归档', () { expect( buildMeetingTranscript( items: [_item(t0.add(const Duration(seconds: 1)), src: ' ')], recordingStartedAt: t0, mode: 'call', ownLanguageCode: 'zh-CN', ), isEmpty, ); }); }