"""`pyproject.toml` の設定検証(PLAN_02 ステップ 1 / ENV_01 技術スタック).
ステップ 1 の完了条件は「`pyproject.toml`(uv・Ruff・pytest 設定)」であり,
採用技術は ENV_01 のとおり Python 3.12・PySide6・opencv-python(4.x)・NumPy・pytest・Ruff.
"""
import tomllib
from pathlib import Path
import pytest
@pytest.fixture(scope="session")
def pyproject(repo_root: Path) -> dict:
return tomllib.loads((repo_root / "pyproject.toml").read_text(encoding="utf-8"))
class TestProjectMetadata:
def test_project_name(self, pyproject: dict) -> None:
assert pyproject["project"]["name"] == "tiasjudge"
def test_requires_python_312(self, pyproject: dict) -> None:
"""ENV_01: 言語は Python 3.12."""
assert ">=3.12" in pyproject["project"]["requires-python"]
def test_python_version_file_pins_312(self, repo_root: Path) -> None:
version_file = repo_root / ".python-version"
assert version_file.is_file()
assert version_file.read_text(encoding="utf-8").strip().startswith("3.12")
@pytest.mark.parametrize("package", ["numpy", "opencv-python", "pyside6"])
def test_runtime_dependencies_declared(self, pyproject: dict, package: str) -> None:
declared = [
dependency.split(">")[0].split("<")[0].split("=")[0].strip().lower()
for dependency in pyproject["project"]["dependencies"]
]
assert package in declared
def test_opencv_is_pinned_to_major_version_4(self, pyproject: dict) -> None:
"""ENV_01: `cv2.ml.SVM` の挙動同一性のため OpenCV 4.x 系に固定する."""
opencv = next(
dependency
for dependency in pyproject["project"]["dependencies"]
if dependency.lower().startswith("opencv-python")
)
assert "<5" in opencv.replace(" ", "")
@pytest.mark.parametrize("package", ["pytest", "ruff"])
def test_dev_dependencies_declared(self, pyproject: dict, package: str) -> None:
dev = [
dependency.split(">")[0].split("<")[0].split("=")[0].strip().lower()
for dependency in pyproject["dependency-groups"]["dev"]
]
assert package in dev
class TestToolConfiguration:
def test_pytest_testpaths_points_to_tests(self, pyproject: dict) -> None:
assert pyproject["tool"]["pytest"]["ini_options"]["testpaths"] == ["tests"]
def test_ruff_targets_py312(self, pyproject: dict) -> None:
assert pyproject["tool"]["ruff"]["target-version"] == "py312"
def test_ruff_lint_rules_selected(self, pyproject: dict) -> None:
selected = set(pyproject["tool"]["ruff"]["lint"]["select"])
assert {"E", "F", "I"} <= selected
def test_wheel_packages_use_src_layout(self, pyproject: dict) -> None:
packages = pyproject["tool"]["hatch"]["build"]["targets"]["wheel"]["packages"]
assert packages == ["src/tiasjudge"]
class TestLockFile:
def test_uv_lock_exists(self, repo_root: Path) -> None:
"""ENV_01: 依存の完全な再現のため `uv.lock` を管理する."""
assert (repo_root / "uv.lock").is_file()
def test_uv_lock_is_not_git_ignored(self, repo_root: Path) -> None:
import subprocess
result = subprocess.run(
["git", "check-ignore", "uv.lock"],
cwd=repo_root,
capture_output=True,
text=True,
)
assert result.returncode != 0, "uv.lock が .gitignore で除外されている"