# -*- coding: utf-8 -*- """media_assets 共享媒体资源解析测试。""" from __future__ import annotations import platform import zipfile from pathlib import Path from unittest.mock import patch import pytest from jiangchang_skill_core import media_assets as ma def _make_complete_assets(root: Path, *, with_ffmpeg: bool = True, with_ffprobe: bool = True) -> None: (root / "music" / "calm").mkdir(parents=True) (root / "fonts").mkdir(parents=True) (root / "watermark").mkdir(parents=True) bin_key = ma._platform_bin_key() bin_dir = root / "bin" / bin_key bin_dir.mkdir(parents=True) if with_ffmpeg: (bin_dir / ma._tool_filename("ffmpeg")).write_bytes(b"ffmpeg") if with_ffprobe: (bin_dir / ma._tool_filename("ffprobe")).write_bytes(b"ffprobe") def test_media_assets_root_env_priority(tmp_path: Path) -> None: custom = tmp_path / "custom-media" custom.mkdir() env = { "MEDIA_ASSETS_ROOT": str(custom), "JIANGCHANG_DATA_ROOT": str(tmp_path / "data"), } assert ma.resolve_media_assets_root(env=env) == custom def test_default_media_assets_path(tmp_path: Path) -> None: env = {"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")} expected = tmp_path / "data" / "shared" / "media-assets" assert ma.resolve_media_assets_root(env=env) == expected def test_complete_assets_skips_download(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: root = tmp_path / "data" / "shared" / "media-assets" _make_complete_assets(root) def _fail_download(*args, **kwargs): raise AssertionError("download should not be called") monkeypatch.setattr(ma, "_try_download_via_zip", _fail_download) monkeypatch.setattr(ma, "_try_download_via_git", _fail_download) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert status.ready is True assert status.warnings == [] assert status.source == "local" def test_missing_ffmpeg_warning(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: root = tmp_path / "data" / "shared" / "media-assets" _make_complete_assets(root, with_ffmpeg=False) monkeypatch.setattr(ma, "_try_download_via_zip", lambda *a, **k: False) monkeypatch.setattr(ma, "_try_download_via_git", lambda *a, **k: False) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert status.ready is False assert "ffmpeg_missing" in status.warnings def test_missing_ffprobe_warning(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: root = tmp_path / "data" / "shared" / "media-assets" _make_complete_assets(root, with_ffprobe=False) monkeypatch.setattr(ma, "_try_download_via_zip", lambda *a, **k: False) monkeypatch.setattr(ma, "_try_download_via_git", lambda *a, **k: False) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert status.ready is False assert "ffprobe_missing" in status.warnings def test_pick_background_music_stable_first(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: root = tmp_path / "data" / "shared" / "media-assets" _make_complete_assets(root) music = root / "music" (music / "b-upbeat.mp3").write_bytes(b"mp3") (music / "calm" / "a-calm.mp3").write_bytes(b"mp3") (music / "z-last.wav").write_bytes(b"wav") monkeypatch.setattr( ma, "ensure_media_assets", lambda *a, **k: ma._inspect_media_assets(root, source="local"), ) picked = ma.pick_background_music(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert picked == music / "b-upbeat.mp3" def _build_archive_zip(dest: Path, inner_name: str) -> None: with zipfile.ZipFile(dest, "w") as zf: zf.writestr(f"{inner_name}/README.md", "# media-assets\n") zf.writestr(f"{inner_name}/music/.keep", "") zf.writestr(f"{inner_name}/fonts/.keep", "") zf.writestr(f"{inner_name}/watermark/.keep", "") bin_key = ma._platform_bin_key() zf.writestr( f"{inner_name}/bin/{bin_key}/{ma._tool_filename('ffmpeg')}", b"ffmpeg", ) zf.writestr( f"{inner_name}/bin/{bin_key}/{ma._tool_filename('ffprobe')}", b"ffprobe", ) def test_zip_wrapper_layout_detected(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: data_root = tmp_path / "data" shared = data_root / "shared" target = shared / "media-assets" archive = tmp_path / "media-assets.zip" _build_archive_zip(archive, "media-assets-main") def _fake_download(url: str, dest: Path) -> None: dest.write_bytes(archive.read_bytes()) monkeypatch.setattr(ma, "_download_zip", _fake_download) monkeypatch.setattr(ma, "_find_git_executable", lambda: None) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(data_root)}) assert target.is_dir() assert status.ready is True assert status.source == "zip" assert "media_assets_zip_invalid_layout" not in status.warnings def test_zip_download_failure_returns_warning(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: import urllib.error def _raise_url_error(url: str, dest: Path) -> None: raise urllib.error.URLError("network down") monkeypatch.setattr(ma, "_download_zip", _raise_url_error) monkeypatch.setattr(ma, "_find_git_executable", lambda: None) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert status.ready is False assert "media_assets_zip_download_failed" in status.warnings assert "git_not_available" in status.warnings def test_git_not_available_when_zip_fails(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: import urllib.error monkeypatch.setattr( ma, "_download_zip", lambda url, dest: (_ for _ in ()).throw(urllib.error.URLError("fail")), ) monkeypatch.setattr(ma, "_find_git_executable", lambda: None) status = ma.ensure_media_assets(env={"JIANGCHANG_DATA_ROOT": str(tmp_path / "data")}) assert "git_not_available" in status.warnings def test_media_assets_root_override_no_download(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: custom = tmp_path / "override" custom.mkdir() def _fail_download(*args, **kwargs): raise AssertionError("download should not be called when MEDIA_ASSETS_ROOT is set") monkeypatch.setattr(ma, "_try_download_via_zip", _fail_download) status = ma.ensure_media_assets(env={"MEDIA_ASSETS_ROOT": str(custom)}) assert status.root == custom assert status.exists is True @pytest.mark.skipif(platform.system().lower() != "windows", reason="Windows default path check") def test_windows_default_data_root() -> None: with patch.dict("os.environ", {}, clear=True): root = ma.resolve_media_assets_root() assert root == Path(r"D:\jiangchang-data") / "shared" / "media-assets"