""" 验证站点资源版本选择、下载与安装行为。 """ from pathlib import Path import pytest from app.adapters.system.resource import ( ResourceHelper, configure_resource_version_provider, ) from app.runtime.config import settings from app.startup.composition import resource as resource_composition from app.startup.initializers import modules as modules_initializer ROOT_DIR = Path(__file__).resolve().parents[1] def test_resource_helper_uses_v3_only(): """ 在线资源更新器必须只请求 V3 清单、目录和站点索引文件。 """ assert settings.VERSION_FLAG == "v3" assert settings.RESOURCE_VERSION_FLAG == "v3" assert ResourceHelper._repo.endswith("/package.v3.json") assert ResourceHelper._files_api.endswith("/resources.v3") assert ResourceHelper._get_needed_files()[0] == "user.sites.v3.bin" assert ResourceHelper._resource_target == Path("app/application/site") @pytest.mark.parametrize( ("system", "machine", "gil_disabled", "expected"), [ ("Linux", "x86_64", 0, "sites.cpython-314-x86_64-linux-gnu.so"), ("Linux", "aarch64", 1, "sites.cpython-314t-aarch64-linux-gnu.so"), ("Darwin", "arm64", 1, "sites.cpython-314t-darwin.so"), ("Windows", "AMD64", 1, "sites.cp314t-win_amd64.pyd"), ], ) def test_resource_helper_selects_runtime_abi( monkeypatch, system, machine, gil_disabled, expected, ): """ 资源下载文件名必须区分普通解释器与 free-threaded ABI。 """ monkeypatch.setattr("app.adapters.system.resource.platform.system", lambda: system) monkeypatch.setattr("app.adapters.system.resource.platform.machine", lambda: machine) monkeypatch.setattr( "app.adapters.system.resource.sysconfig.get_config_var", lambda name: gil_disabled if name == "Py_GIL_DISABLED" else None, ) assert ResourceHelper._get_needed_files()[-1] == expected def test_resource_helper_preserves_no_argument_check_contract(monkeypatch): """ 旧插件无参数调用 check 时应使用启动层注入版本,不反向导入站点应用。 """ provider_calls = [] def provide_versions() -> tuple[str, str]: """ 记录资源更新器是否读取了组合根注入的版本。 """ provider_calls.append(True) return "2.4.10", "3.0.2" configure_resource_version_provider(provide_versions) monkeypatch.setattr(ResourceHelper, "_load_resource_info", lambda _self: None) monkeypatch.setattr("app.adapters.system.resource.settings.AUTO_UPDATE_RESOURCE", True) monkeypatch.setattr( "app.adapters.system.resource.SystemUtils.is_frozen", lambda: False, ) assert ResourceHelper().check() is False assert provider_calls == [True] def test_startup_resource_hook_only_configures_version_provider(monkeypatch): """ 启动组合层只注册资源版本读取器,不在进程启动后下载或重启。 """ providers = [] monkeypatch.setattr( resource_composition, "configure_resource_version_provider", lambda provider: providers.append(provider), ) resource_composition.configure_site_resource_versions(lambda: ("1", "2")) assert len(providers) == 1 assert providers[0]() == ("1", "2") def test_startup_resource_update_hook_is_noop(monkeypatch): """ 旧启动入口保留兼容性,但不再触发资源检查、安装或重启。 """ modules_initializer.update_resources() def test_install_and_docker_paths_do_not_reference_v2_resources(): """ 本地安装和 Docker 资源流程不得包含 V2 资源回退。 """ paths = [ ROOT_DIR / "scripts" / "local_setup.py", ROOT_DIR / "docker" / "Dockerfile", ROOT_DIR / "docker" / "update.sh", ] for path in paths: content = path.read_text(encoding="utf-8") assert "resources.v2" not in content assert "user.sites.v2.bin" not in content assert "resources.v3" in content assert "app/application/site" in content def test_docker_entrypoint_does_not_sync_updater_as_a_special_case(): """ 容器控制脚本必须由 launcher 统一固化,不能单独替换 updater。 """ content = (ROOT_DIR / "docker" / "entrypoint.sh").read_text(encoding="utf-8") assert "mp_update.sh" not in content assert 'source "${MP_CONTROL_DIR:-/usr/local/lib/moviepilot/control}/update.sh"' in content def test_v3_release_workflows_use_main_wiki_and_isolated_images(): """ V3 正式版和 Beta 构建应读取主 Wiki 分支并保持镜像仓库隔离。 """ build_workflow = (ROOT_DIR / ".github" / "workflows" / "build-v3.yml").read_text(encoding="utf-8") beta_workflow = (ROOT_DIR / ".github" / "workflows" / "beta.yml").read_text(encoding="utf-8") assert "name: MoviePilot Builder v3" in build_workflow assert " - v3" in build_workflow assert " repository: jxxghp/MoviePilot-Wiki\n ref: main" in build_workflow assert "${{ secrets.DOCKER_USERNAME }}/moviepilot\n" in build_workflow assert "${{ secrets.DOCKER_USERNAME }}/moviepilot-v3" in build_workflow assert "ghcr.io/${{ github.repository }}-v3" in build_workflow assert "${{ secrets.DOCKER_USERNAME }}/moviepilot-v2" not in build_workflow assert "python3 scripts/release_notes.py" in build_workflow assert '--source "$SOURCE_COMMIT"' in build_workflow assert " repository: jxxghp/MoviePilot-Wiki\n ref: main" in beta_workflow assert "${{ secrets.DOCKER_USERNAME }}/moviepilot-v3" in beta_workflow assert "ghcr.io/${{ github.repository }}-v3" in beta_workflow assert "${{ secrets.DOCKER_USERNAME }}/moviepilot-v2" not in beta_workflow