ForcePilot/backend/test/unit/agents/toolkits/buildin/test_asr.py
Kris b3bdb4fa2f feat: 新增TTS语音合成、音视频解析能力
1. 新增内置TTS工具,支持OpenAI兼容语音合成服务
2. 新增ASR音频解析和视频解析处理器
3. 扩展文件解析支持范围,添加音视频格式支持
4. 新增ffmpeg音视频处理基础工具库
5. 补充相关单元测试用例
2026-06-13 20:33:40 +08:00

124 lines
4.6 KiB
Python

"""ASR 解析器单元测试。"""
import os
from unittest.mock import MagicMock, patch
import pytest
from yuxi.knowledge.parser.asr import ASRProcessor
from yuxi.knowledge.parser.base import DocumentProcessorException
@pytest.fixture
def asr():
return ASRProcessor()
class TestASRProcessorBasics:
def test_service_name(self, asr):
assert asr.get_service_name() == "asr"
def test_supported_extensions(self, asr):
exts = asr.get_supported_extensions()
assert ".mp3" in exts
assert ".wav" in exts
assert ".m4a" in exts
assert ".flac" in exts
def test_supports_file_type(self, asr):
assert asr.supports_file_type(".mp3")
assert asr.supports_file_type(".wav")
assert not asr.supports_file_type(".pdf")
class TestSegmentsToMarkdown:
def test_basic_formatting(self):
segments = [
{"start": 0, "end": 5, "text": "Hello world"},
{"start": 65, "end": 70, "text": "Second segment"},
]
result = ASRProcessor._segments_to_markdown(segments, "test.mp3")
assert "# 音频转写" in result
assert "> 来源: test.mp3" in result
assert "**[00:00]** Hello world" in result
assert "**[01:05]** Second segment" in result
def test_empty_segments(self):
result = ASRProcessor._segments_to_markdown([], "test.mp3")
assert "# 音频转写" in result
assert "> 来源: test.mp3" in result
def test_skip_empty_text(self):
segments = [
{"start": 0, "end": 5, "text": ""},
{"start": 5, "end": 10, "text": " "},
{"start": 10, "end": 15, "text": "valid"},
]
result = ASRProcessor._segments_to_markdown(segments, "test.mp3")
assert "valid" in result
assert result.count("**[") == 1
class TestProcessFile:
@patch("yuxi.knowledge.parser.asr.probe_audio_duration", return_value=60.0)
@patch("yuxi.knowledge.parser.asr.convert_to_wav")
@patch("yuxi.knowledge.parser.asr.ASRProcessor._transcribe")
def test_success(self, mock_transcribe, mock_convert, mock_duration, asr, tmp_path):
test_file = tmp_path / "test.mp3"
test_file.write_bytes(b"fake audio data")
mock_transcribe.return_value = [{"start": 0, "end": 5, "text": "Hello"}]
result = asr.process_file(str(test_file))
assert "# 音频转写" in result
assert "Hello" in result
@patch("yuxi.knowledge.parser.asr.probe_audio_duration", return_value=60.0)
def test_file_too_large(self, mock_duration, asr, tmp_path):
test_file = tmp_path / "big.mp3"
test_file.write_bytes(b"x" * (16 * 1024 * 1024 + 1))
with pytest.raises(DocumentProcessorException, match="大小"):
asr.process_file(str(test_file))
@patch("yuxi.knowledge.parser.asr.probe_audio_duration", return_value=1500.0)
def test_duration_too_long(self, mock_duration, asr, tmp_path):
test_file = tmp_path / "long.mp3"
test_file.write_bytes(b"fake audio")
with pytest.raises(DocumentProcessorException, match="时长"):
asr.process_file(str(test_file))
@patch("yuxi.knowledge.parser.asr.probe_audio_duration", return_value=60.0)
@patch("yuxi.knowledge.parser.asr.convert_to_wav")
def test_unsupported_asr_provider(self, mock_convert, mock_duration, asr, tmp_path):
test_file = tmp_path / "test.mp3"
test_file.write_bytes(b"fake audio")
with pytest.raises(DocumentProcessorException, match="不支持的 ASR 提供者"):
asr.process_file(str(test_file), {"asr_provider": "unknown"})
class TestCheckHealth:
@patch.dict(os.environ, {"ASR_PROVIDER": "whisper", "WHISPER_API_KEY": "test-key"})
def test_whisper_configured(self, asr):
result = asr.check_health()
assert result["status"] == "healthy"
@patch.dict(os.environ, {"ASR_PROVIDER": "whisper"}, clear=False)
def test_whisper_not_configured(self, asr):
with patch.dict(os.environ, {"WHISPER_API_KEY": ""}):
result = asr.check_health()
assert result["status"] == "unavailable"
@patch.dict(os.environ, {"ASR_PROVIDER": "funasr"})
@patch("yuxi.knowledge.parser.asr.httpx.Client")
def test_funasr_unreachable(self, mock_client_cls, asr):
mock_client = MagicMock()
mock_client.__enter__ = MagicMock(return_value=mock_client)
mock_client.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = Exception("connection refused")
mock_client_cls.return_value = mock_client
result = asr.check_health()
assert result["status"] == "unhealthy"