4fd1d70b58
1. 调整测试用例中的路径比较逻辑,统一替换反斜杠为正斜杠适配Windows平台 2. 将原生echo/true/false命令替换为跨平台的python执行命令,避免环境依赖问题
193 lines
8.3 KiB
Python
193 lines
8.3 KiB
Python
"""Tests for ops.bumpversion module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from pyflowx.ops import bumpversion
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def auto_use_tmp_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""自动使用临时路径."""
|
|
monkeypatch.chdir(tmp_path)
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# bump_file_version
|
|
# ---------------------------------------------------------------------- #
|
|
class TestBumpFileVersion:
|
|
"""Test bump_file_version function."""
|
|
|
|
@pytest.mark.parametrize(
|
|
("part", "expected"),
|
|
[("patch", "1.2.4"), ("minor", "1.3.0"), ("major", "2.0.0")],
|
|
)
|
|
def test_bump_pyproject(self, tmp_path: Path, part: str, expected: str) -> None:
|
|
"""pyproject.toml 三种 part 递增."""
|
|
f = tmp_path / "pyproject.toml"
|
|
f.write_text('version = "1.2.3"', encoding="utf-8")
|
|
assert bumpversion.bump_file_version(f, part) == expected # type: ignore[arg-type]
|
|
assert f'version = "{expected}"' in f.read_text(encoding="utf-8")
|
|
|
|
@pytest.mark.parametrize(
|
|
("part", "expected"),
|
|
[("patch", "1.2.4"), ("minor", "1.3.0"), ("major", "2.0.0")],
|
|
)
|
|
def test_bump_init_py(self, tmp_path: Path, part: str, expected: str) -> None:
|
|
"""__init__.py 三种 part 递增."""
|
|
f = tmp_path / "__init__.py"
|
|
f.write_text('__version__ = "1.2.3"', encoding="utf-8")
|
|
assert bumpversion.bump_file_version(f, part) == expected # type: ignore[arg-type]
|
|
assert f'__version__ = "{expected}"' in f.read_text(encoding="utf-8")
|
|
|
|
def test_prerelease_and_build_metadata_stripped(self, tmp_path: Path) -> None:
|
|
"""prerelease 和 build metadata 应被清除."""
|
|
f = tmp_path / "pyproject.toml"
|
|
f.write_text('version = "1.2.3-alpha.1+build.123"', encoding="utf-8")
|
|
assert bumpversion.bump_file_version(f, "patch") == "1.2.4"
|
|
content = f.read_text(encoding="utf-8")
|
|
assert "alpha" not in content
|
|
assert "build" not in content
|
|
|
|
def test_dependencies_not_modified(self, tmp_path: Path) -> None:
|
|
"""只更新 project version, 不动 dependencies 中的版本号."""
|
|
f = tmp_path / "pyproject.toml"
|
|
f.write_text(
|
|
'[project]\nversion = "1.0.0"\ndependencies = ["lib >= 2.0.0", "other >= 3.0.0"]\n',
|
|
encoding="utf-8",
|
|
)
|
|
assert bumpversion.bump_file_version(f, "patch") == "1.0.1"
|
|
content = f.read_text(encoding="utf-8")
|
|
assert 'version = "1.0.1"' in content
|
|
assert "lib >= 2.0.0" in content
|
|
assert "other >= 3.0.0" in content
|
|
|
|
def test_no_version_pattern_returns_none(self, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
|
|
"""未匹配到版本号模式返回 None (支持类型但无版本 / 不支持的文件类型)."""
|
|
f1 = tmp_path / "__init__.py"
|
|
f1.write_text("# no version here", encoding="utf-8")
|
|
assert bumpversion.bump_file_version(f1, "patch") is None
|
|
assert "未找到版本号模式" in capsys.readouterr().out
|
|
|
|
f2 = tmp_path / "test.txt"
|
|
f2.write_text("no version here", encoding="utf-8")
|
|
assert bumpversion.bump_file_version(f2, "patch") is None
|
|
|
|
def test_read_directory_raises(self, tmp_path: Path) -> None:
|
|
"""读取目录 (名为 __init__.py) 应抛异常."""
|
|
f = tmp_path / "__init__.py"
|
|
f.mkdir()
|
|
with pytest.raises(Exception): # noqa: B017
|
|
bumpversion.bump_file_version(f, "patch")
|
|
|
|
def test_write_failure_raises(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""写入失败应抛 OSError."""
|
|
f = tmp_path / "__init__.py"
|
|
f.write_text('__version__ = "1.0.0"', encoding="utf-8")
|
|
|
|
def raise_oserror(*_args: object, **_kwargs: object) -> None:
|
|
raise OSError("write failed")
|
|
|
|
monkeypatch.setattr(Path, "write_text", raise_oserror)
|
|
with pytest.raises(OSError, match="write failed"):
|
|
bumpversion.bump_file_version(f, "patch")
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# bump_project_version (核心 bug 修复: 不同步文件统一同步)
|
|
# ---------------------------------------------------------------------- #
|
|
class TestBumpProjectVersion:
|
|
"""Test bump_project_version function."""
|
|
|
|
@staticmethod
|
|
def _mock_subprocess(monkeypatch: pytest.MonkeyPatch) -> list[list[str]]:
|
|
"""Mock subprocess.run, 返回调用记录列表."""
|
|
calls: list[list[str]] = []
|
|
|
|
def fake_run(cmd: list[str], **_kwargs: object) -> subprocess.CompletedProcess[bytes]:
|
|
calls.append(cmd)
|
|
return subprocess.CompletedProcess(cmd, 0, b"", b"")
|
|
|
|
monkeypatch.setattr(subprocess, "run", fake_run)
|
|
return calls
|
|
|
|
def test_unsynced_files_synchronized(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
) -> None:
|
|
"""核心 bug 修复: 不同步的文件应统一同步到同一新版本号.
|
|
|
|
场景: __init__.py = 0.4.5, pyproject.toml = 0.3.5 (历史不同步)
|
|
期望: bump patch 后两者都变为 0.4.6 (取最大值 0.4.5 作为基准 +1)
|
|
"""
|
|
init_file = tmp_path / "src" / "pkg" / "__init__.py"
|
|
init_file.parent.mkdir(parents=True)
|
|
init_file.write_text('__version__ = "0.4.5"', encoding="utf-8")
|
|
pyproj = tmp_path / "pyproject.toml"
|
|
pyproj.write_text('version = "0.3.5"', encoding="utf-8")
|
|
|
|
calls = self._mock_subprocess(monkeypatch)
|
|
|
|
result = bumpversion.bump_project_version("patch")
|
|
|
|
assert result == "0.4.6"
|
|
assert '__version__ = "0.4.6"' in init_file.read_text(encoding="utf-8")
|
|
assert 'version = "0.4.6"' in pyproj.read_text(encoding="utf-8")
|
|
out = capsys.readouterr().out
|
|
assert "基准版本: 0.4.5" in out
|
|
assert "新版本: 0.4.6" in out
|
|
|
|
add_calls = [c for c in calls if c[:2] == ["git", "add"]]
|
|
assert len(add_calls) == 1
|
|
# 跨平台: Windows 上 Path 转换为反斜杠, 统一用正斜杠比较
|
|
init_path = str(init_file.relative_to(tmp_path)).replace("\\", "/")
|
|
assert init_path in [arg.replace("\\", "/") for arg in add_calls[0]]
|
|
assert "pyproject.toml" in add_calls[0]
|
|
assert "." not in add_calls[0][2:]
|
|
|
|
tag_calls = [c for c in calls if c[:2] == ["git", "tag"]]
|
|
assert len(tag_calls) == 1
|
|
assert "v0.4.6" in tag_calls[0]
|
|
|
|
def test_no_files_returns_none(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
"""无版本号文件返回 None."""
|
|
assert bumpversion.bump_project_version("patch") is None
|
|
assert "未找到包含版本号的文件" in capsys.readouterr().out
|
|
|
|
def test_files_without_version_returns_none(
|
|
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
) -> None:
|
|
"""有文件但所有文件都无版本号返回 None."""
|
|
f = tmp_path / "__init__.py"
|
|
f.write_text("# no version here", encoding="utf-8")
|
|
self._mock_subprocess(monkeypatch)
|
|
|
|
assert bumpversion.bump_project_version("patch") is None
|
|
assert "未能从任何文件读取版本号" in capsys.readouterr().out
|
|
|
|
def test_no_tag_skips_tag_creation(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""no_tag=True 跳过 tag 创建."""
|
|
pyproj = tmp_path / "pyproject.toml"
|
|
pyproj.write_text('version = "1.0.0"', encoding="utf-8")
|
|
|
|
calls = self._mock_subprocess(monkeypatch)
|
|
|
|
assert bumpversion.bump_project_version("patch", no_tag=True) == "1.0.1"
|
|
assert not any(c[:2] == ["git", "tag"] for c in calls)
|
|
|
|
def test_ignored_dirs_excluded(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
""".venv 等忽略目录中的版本号文件不被处理."""
|
|
venv_init = tmp_path / ".venv" / "lib" / "pkg" / "__init__.py"
|
|
venv_init.parent.mkdir(parents=True)
|
|
venv_init.write_text('__version__ = "0.1.0"', encoding="utf-8")
|
|
pyproj = tmp_path / "pyproject.toml"
|
|
pyproj.write_text('version = "1.0.0"', encoding="utf-8")
|
|
|
|
self._mock_subprocess(monkeypatch)
|
|
|
|
assert bumpversion.bump_project_version("patch") == "1.0.1"
|
|
assert venv_init.read_text(encoding="utf-8") == '__version__ = "0.1.0"'
|