Files
MoviePilot/tests/test_local_setup_update.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

181 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
本地 CLI 更新遇到源码改动时的确认与清理测试。
"""
from __future__ import annotations
import importlib.util
import subprocess
import uuid
from pathlib import Path
import pytest
MODULE_PATH = Path(__file__).resolve().parents[1] / "scripts" / "local_setup.py"
def load_local_setup_module():
"""
加载独立的本地安装脚本模块,避免共享全局配置。
"""
module_name = f"moviepilot_local_setup_update_{uuid.uuid4().hex}"
spec = importlib.util.spec_from_file_location(module_name, MODULE_PATH)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_dirty_update_clears_tracked_changes_after_confirmation(monkeypatch):
"""
确认清理后应重置已跟踪源码改动,供后续 Git 更新继续。
"""
module = load_local_setup_module()
prompts: list[tuple[str, bool]] = []
commands: list[list[str]] = []
monkeypatch.setattr(module, "_git_output", lambda *_args: " M app/example.py\n")
monkeypatch.setattr(
module,
"_prompt_yes_no",
lambda label, default: prompts.append((label, default)) or True,
)
monkeypatch.setattr(
module,
"run",
lambda command, cwd=None: commands.append(command),
)
module._ensure_git_clean()
assert prompts == [
(
"检测到当前仓库有未提交的源码改动:app/example.py,是否清空本地改动并继续更新",
False,
)
]
assert commands == [["git", "reset", "--hard", "HEAD"]]
def test_dirty_update_stops_when_local_change_cleanup_is_rejected(monkeypatch):
"""
拒绝清理时应取消更新且不执行任何 Git 写操作。
"""
module = load_local_setup_module()
commands: list[list[str]] = []
monkeypatch.setattr(module, "_git_output", lambda *_args: " M app/example.py\n")
monkeypatch.setattr(module, "_prompt_yes_no", lambda *_args, **_kwargs: False)
monkeypatch.setattr(
module,
"run",
lambda command, cwd=None: commands.append(command),
)
with pytest.raises(RuntimeError, match="已取消更新"):
module._ensure_git_clean()
assert commands == []
def test_dirty_update_stops_without_an_interactive_input(monkeypatch):
"""
没有可用输入时应取消更新,而不是默认清除源码改动。
"""
module = load_local_setup_module()
commands: list[list[str]] = []
monkeypatch.setattr(module, "_git_output", lambda *_args: " M app/example.py\n")
def raise_eof(*_args, **_kwargs):
"""
模拟没有可用终端输入。
"""
raise EOFError
monkeypatch.setattr(module, "_prompt_yes_no", raise_eof)
monkeypatch.setattr(
module,
"run",
lambda command, cwd=None: commands.append(command),
)
with pytest.raises(RuntimeError, match="不支持交互确认"):
module._ensure_git_clean()
assert commands == []
@pytest.fixture
def moved_tag_repository(tmp_path):
"""
用临时本地仓库模拟远端重建同名标签,不访问外部网络。
"""
remote = tmp_path / "remote"
checkout = tmp_path / "checkout"
remote.mkdir()
def git(path, *args):
"""
隔离 Git 身份和签名配置,执行测试仓库操作。
"""
return subprocess.check_output(
["git", "-c", "user.name=CLI Test", "-c", "user.email=cli@example.test",
"-c", "commit.gpgsign=false", "-c", "tag.gpgsign=false", "-C", str(path), *args],
text=True,
).strip()
git(remote, "init", "-b", "v3")
git(remote, "commit", "--allow-empty", "-m", "initial")
git(remote, "tag", "v3.1.0")
old_sha = git(remote, "rev-parse", "HEAD")
git(tmp_path, "clone", str(remote), str(checkout))
git(checkout, "tag", "local-only")
git(remote, "commit", "--allow-empty", "-m", "rebuild")
git(remote, "tag", "--force", "v3.1.0")
return checkout, git, old_sha, git(remote, "rev-parse", "HEAD")
@pytest.mark.parametrize("ref", ["latest", "v3", "v3.1.0"])
def test_update_overwrites_moved_remote_tag(monkeypatch, moved_tag_repository, ref):
"""
同名标签随远端更新,同时保留远端没有的本地标签。
"""
checkout, git, old_sha, new_sha = moved_tag_repository
module = load_local_setup_module()
monkeypatch.setattr(module, "ROOT", checkout)
assert module._update_backend_ref(ref) == ("v3" if ref == "latest" else ref)
assert git(checkout, "rev-parse", "v3.1.0") == new_sha
assert git(checkout, "rev-parse", "HEAD") == new_sha
assert git(checkout, "rev-parse", "local-only") == old_sha
def test_update_moved_tag_does_not_reset_diverged_branch(monkeypatch, moved_tag_repository):
"""
强制同步标签不得覆盖分叉分支上的本地提交。
"""
checkout, git, _, new_sha = moved_tag_repository
git(checkout, "commit", "--allow-empty", "-m", "local work")
local_sha = git(checkout, "rev-parse", "HEAD")
module = load_local_setup_module()
monkeypatch.setattr(module, "ROOT", checkout)
with pytest.raises(subprocess.CalledProcessError):
module._update_backend_ref("latest")
assert git(checkout, "rev-parse", "HEAD") == local_sha
assert git(checkout, "rev-parse", "v3.1.0") == new_sha
def test_offline_update_preserves_local_tag(monkeypatch, moved_tag_repository):
"""
离线更新只使用已验证的本地标签,即使远端已更换标签。
"""
checkout, git, old_sha, _ = moved_tag_repository
git(checkout, "remote", "remove", "origin")
module = load_local_setup_module()
monkeypatch.setattr(module, "ROOT", checkout)
assert module._update_backend_ref("v3.1.0", fetch=False) == "v3.1.0"
assert git(checkout, "rev-parse", "HEAD") == old_sha