220 lines
8.7 KiB
Python
220 lines
8.7 KiB
Python
"""Tests for cli.piptool module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
import pyflowx as px
|
|
from pyflowx.cli import piptool
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# pip_uninstall
|
|
# ---------------------------------------------------------------------- #
|
|
class TestPipUninstall:
|
|
"""Test pip_uninstall function."""
|
|
|
|
def test_pip_uninstall_single_package(self) -> None:
|
|
"""Should uninstall single package."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_uninstall(["numpy"])
|
|
# Should call pip uninstall
|
|
assert mock_run.called
|
|
|
|
def test_pip_uninstall_multiple_packages(self) -> None:
|
|
"""Should uninstall multiple packages."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_uninstall(["numpy", "pandas", "scipy"])
|
|
# Should call pip uninstall
|
|
assert mock_run.called
|
|
|
|
def test_pip_uninstall_with_wildcard(self) -> None:
|
|
"""Should handle wildcard in package name."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_uninstall(["numpy*"])
|
|
assert mock_run.called
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# pip_reinstall
|
|
# ---------------------------------------------------------------------- #
|
|
class TestPipReinstall:
|
|
"""Test pip_reinstall function."""
|
|
|
|
def test_pip_reinstall_online(self) -> None:
|
|
"""Should reinstall packages online."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_reinstall(["numpy"], offline=False)
|
|
assert mock_run.called
|
|
|
|
def test_pip_reinstall_offline(self) -> None:
|
|
"""Should reinstall packages offline."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_reinstall(["numpy"], offline=True)
|
|
# Should call pip install with offline flags
|
|
assert mock_run.called
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# pip_download
|
|
# ---------------------------------------------------------------------- #
|
|
class TestPipDownload:
|
|
"""Test pip_download function."""
|
|
|
|
def test_pip_download_online(self) -> None:
|
|
"""Should download packages online."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_download(["numpy"], offline=False)
|
|
assert mock_run.called
|
|
|
|
def test_pip_download_offline(self) -> None:
|
|
"""Should download packages offline."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
piptool.pip_download(["numpy"], offline=True)
|
|
# Should call pip download with offline flags
|
|
assert mock_run.called
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# pip_freeze
|
|
# ---------------------------------------------------------------------- #
|
|
class TestPipFreeze:
|
|
"""Test pip_freeze function."""
|
|
|
|
def test_pip_freeze_creates_file(self, tmp_path: Path) -> None:
|
|
"""Should create requirements.txt file."""
|
|
with patch("subprocess.run") as mock_run, patch.object(Path, "cwd", return_value=tmp_path):
|
|
mock_run.return_value = MagicMock(stdout="numpy==1.0.0\npandas==2.0.0\n", returncode=0)
|
|
piptool.pip_freeze()
|
|
# Should create requirements.txt
|
|
req_file = tmp_path / "requirements.txt"
|
|
# Note: The actual implementation might write to current directory
|
|
|
|
def test_pip_freeze_calls_subprocess(self) -> None:
|
|
"""Should call pip freeze."""
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_run.return_value = MagicMock(stdout="", returncode=0)
|
|
piptool.pip_freeze()
|
|
assert mock_run.called
|
|
call_args = mock_run.call_args[0][0]
|
|
assert "pip" in call_args
|
|
assert "freeze" in call_args
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# main function
|
|
# ---------------------------------------------------------------------- #
|
|
class TestMain:
|
|
"""Test main function."""
|
|
|
|
def test_main_install_single_package(self) -> None:
|
|
"""main() should handle install single package."""
|
|
with patch("sys.argv", ["piptool", "i", "numpy"]), patch.object(px, "run") as mock_run:
|
|
piptool.main()
|
|
assert mock_run.called
|
|
graph = mock_run.call_args[0][0]
|
|
specs = graph.all_specs()
|
|
for spec in specs.values():
|
|
assert "pip" in spec.cmd
|
|
assert "install" in spec.cmd
|
|
assert "numpy" in spec.cmd
|
|
|
|
def test_main_install_multiple_packages(self) -> None:
|
|
"""main() should handle install multiple packages."""
|
|
with patch("sys.argv", ["piptool", "i", "numpy", "pandas", "scipy"]), patch.object(px, "run") as mock_run:
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_uninstall_packages(self) -> None:
|
|
"""main() should handle uninstall packages."""
|
|
with patch("sys.argv", ["piptool", "u", "numpy"]), patch.object(px, "run") as mock_run, patch.object(
|
|
piptool, "pip_uninstall"
|
|
):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_reinstall_packages(self) -> None:
|
|
"""main() should handle reinstall packages."""
|
|
with patch("sys.argv", ["piptool", "r", "numpy"]), patch.object(px, "run") as mock_run, patch.object(
|
|
piptool, "pip_reinstall"
|
|
):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_reinstall_offline(self) -> None:
|
|
"""main() should handle reinstall offline."""
|
|
with patch("sys.argv", ["piptool", "r", "numpy", "--offline"]), patch.object(
|
|
px, "run"
|
|
) as mock_run, patch.object(piptool, "pip_reinstall"):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_download_packages(self) -> None:
|
|
"""main() should handle download packages."""
|
|
with patch("sys.argv", ["piptool", "d", "numpy"]), patch.object(px, "run") as mock_run, patch.object(
|
|
piptool, "pip_download"
|
|
):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_download_offline(self) -> None:
|
|
"""main() should handle download offline."""
|
|
with patch("sys.argv", ["piptool", "d", "numpy", "--offline"]), patch.object(
|
|
px, "run"
|
|
) as mock_run, patch.object(piptool, "pip_download"):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_upgrade_pip(self) -> None:
|
|
"""main() should handle upgrade pip."""
|
|
with patch("sys.argv", ["piptool", "up"]), patch.object(px, "run") as mock_run:
|
|
piptool.main()
|
|
assert mock_run.called
|
|
graph = mock_run.call_args[0][0]
|
|
specs = graph.all_specs()
|
|
for spec in specs.values():
|
|
assert "python" in spec.cmd
|
|
assert "-m" in spec.cmd
|
|
assert "pip" in spec.cmd
|
|
assert "install" in spec.cmd
|
|
assert "--upgrade" in spec.cmd
|
|
|
|
def test_main_freeze(self) -> None:
|
|
"""main() should handle freeze."""
|
|
with patch("sys.argv", ["piptool", "f"]), patch.object(px, "run") as mock_run, patch.object(
|
|
piptool, "pip_freeze"
|
|
):
|
|
piptool.main()
|
|
assert mock_run.called
|
|
|
|
def test_main_with_no_args_shows_help(self) -> None:
|
|
"""main() with no args should show help and exit."""
|
|
with patch("sys.argv", ["piptool"]), pytest.raises(SystemExit) as exc_info:
|
|
piptool.main()
|
|
assert exc_info.value.code == 2
|
|
|
|
def test_main_creates_task_specs_with_verbose(self) -> None:
|
|
"""main() should create TaskSpecs with verbose=True."""
|
|
with patch("sys.argv", ["piptool", "i", "numpy"]), patch.object(px, "run") as mock_run:
|
|
piptool.main()
|
|
graph = mock_run.call_args[0][0]
|
|
specs = graph.all_specs()
|
|
for spec in specs.values():
|
|
assert spec.verbose is True
|
|
|
|
def test_main_uses_thread_strategy(self) -> None:
|
|
"""main() should use thread strategy."""
|
|
with patch("sys.argv", ["piptool", "i", "numpy"]), patch.object(px, "run") as mock_run:
|
|
piptool.main()
|
|
assert mock_run.call_args[1]["strategy"] == "thread"
|