import Testing import Foundation @testable import 康康 /// 「记录问诊」端侧 SenseVoice 转写(2026-06-30):转写稿清洗 + 模型位置 + 可用性闸门。 struct SenseVoiceASRTests { // MARK: - 转写稿清洗(纯函数) @Test func cleanStripsSenseVoiceTags() { // SenseVoice 可能在文本前带 <|lang|><|emotion|><|event|><|itn|> 标签 let raw = "<|zh|><|NEUTRAL|><|Speech|><|woitn|>最近胸口闷,上楼梯就喘" #expect(SenseVoiceASRService.cleanTranscript(raw) == "最近胸口闷,上楼梯就喘") } @Test func cleanTrimsWhitespace() { #expect(SenseVoiceASRService.cleanTranscript(" 下周三复查 \n") == "下周三复查") } @Test func cleanLeavesPlainTextUntouched() { let plain = "医生说先做心电图和验血" #expect(SenseVoiceASRService.cleanTranscript(plain) == plain) } @Test func cleanHandlesTagsOnlyAsEmpty() { // 只有标签、没有内容 → 清成空串(上层据此回退 / 提示没听清) #expect(SenseVoiceASRService.cleanTranscript("<|en|><|HAPPY|>").isEmpty) } // MARK: - 模型位置 @Test func modelPathsLiveUnderSenseVoiceDir() { #expect(SenseVoiceASRService.modelDir.lastPathComponent == "SenseVoice") #expect(SenseVoiceASRService.modelFile.lastPathComponent == "model.mnn") #expect(SenseVoiceASRService.tokensFile.lastPathComponent == "tokens.txt") // 与 LLM 模型同根目录(Application Support/Models),互不干扰 #expect(SenseVoiceASRService.modelDir.deletingLastPathComponent() == ModelStore.shared.rootURL) } // MARK: - 可用性闸门 @Test func unavailableWhenEngineNotLinked() { // 本测试构建未链接 sherpa-mnn(桥走桩)→ 无论模型是否就位,isAvailable 必为 false, // 「记录问诊」据此自动回退系统端侧识别(SFSpeech)。 if !SenseVoiceASRService.isModelInstalled { #expect(SenseVoiceASRService.isAvailable == false) } } }