from __future__ import annotations import json import tempfile import unittest from pathlib import Path from biliup_next.core.models import Artifact, Task, utc_now_iso from biliup_next.modules.song_detect.providers.qwen_cli import QwenCliSongDetector class FakeQwenCliAdapter: def __init__(self, returncode: int = 0) -> None: self.returncode = returncode self.last_qwen_cmd: str | None = None def run_song_detect(self, *, qwen_cmd: str, work_dir: Path, prompt: str): # noqa: ANN001 self.last_qwen_cmd = qwen_cmd songs_json_path = work_dir / "songs.json" songs_json_path.write_text( json.dumps( { "songs": [ { "start": "00:01:23,000", "end": "00:03:45,000", "title": "测试歌曲", "artist": "测试歌手", "confidence": 0.93, "evidence": "歌词命中", } ] }, ensure_ascii=False, ), encoding="utf-8", ) return type("Result", (), {"returncode": self.returncode, "stdout": "ok", "stderr": ""})() class SongDetectProviderTests(unittest.TestCase): def test_qwen_cli_provider_generates_json_and_txt_artifacts(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: work_dir = Path(tmpdir) subtitle_path = work_dir / "subtitle.srt" subtitle_path.write_text("1\n00:00:00,000 --> 00:00:03,000\n测试字幕\n", encoding="utf-8") provider = QwenCliSongDetector(adapter=FakeQwenCliAdapter()) task = Task( id="task-1", source_type="local_file", source_path=str(work_dir / "video.mp4"), title="task-1", status="transcribed", created_at=utc_now_iso(), updated_at=utc_now_iso(), ) subtitle = Artifact( id=None, task_id=task.id, artifact_type="subtitle_srt", path=str(subtitle_path), metadata_json=None, created_at=utc_now_iso(), ) songs_json, songs_txt = provider.detect(task, subtitle, {"qwen_cmd": "qwen"}) self.assertEqual(json.loads(songs_json.metadata_json)["provider"], "qwen_cli") self.assertEqual(json.loads(songs_txt.metadata_json)["provider"], "qwen_cli") self.assertTrue(Path(songs_json.path).exists()) self.assertTrue(Path(songs_txt.path).exists()) self.assertIn("测试歌曲", Path(songs_txt.path).read_text(encoding="utf-8")) if __name__ == "__main__": unittest.main()