Public Access
274 lines
9.8 KiB
Python
274 lines
9.8 KiB
Python
"""Unit tests for devx.tools.setup_image."""
|
|
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from devx.tools.setup_image import (
|
|
_build_pip_extra_index_url,
|
|
_fallback_to_setup_ci,
|
|
_install_in_image,
|
|
cli,
|
|
)
|
|
|
|
|
|
class TestBuildPipExtraIndexUrl:
|
|
def test_basic_url(self) -> None:
|
|
url = _build_pip_extra_index_url(
|
|
"git.oblachno.oblachno.fyi",
|
|
"oblachno-oss",
|
|
"emil",
|
|
"tok123",
|
|
)
|
|
assert url == "https://emil:tok123@git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple/"
|
|
|
|
def test_custom_host_org(self) -> None:
|
|
url = _build_pip_extra_index_url(
|
|
"gitea.example.com",
|
|
"my-org",
|
|
"user",
|
|
"secret",
|
|
)
|
|
assert url == "https://user:secret@gitea.example.com/api/packages/my-org/pypi/simple/"
|
|
|
|
|
|
class TestInstallInImage:
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_link_and_install_no_token(self, mock_path: MagicMock, mock_run: MagicMock, tmp_path: Path) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
_install_in_image(str(venv_link), "/opt/venv", "", "host", "org")
|
|
|
|
mock_path.return_value.symlink_to.assert_called_once_with("/opt/venv")
|
|
mock_run.assert_called_once()
|
|
cmd = mock_run.call_args[0][0]
|
|
assert "--no-cache-dir" in cmd
|
|
assert "--no-deps" in cmd
|
|
assert "-e" in cmd
|
|
assert "." in cmd
|
|
# No extras → spec is "."
|
|
assert ".[]" not in " ".join(cmd)
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_link_and_install_with_extras(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_run: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
_install_in_image(str(venv_link), "/opt/venv", "ci,lint", "host", "org")
|
|
|
|
cmd = mock_run.call_args[0][0]
|
|
assert ".[ci,lint]" in cmd
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_install_with_token_sets_pip_extra_index_url(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_run: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(
|
|
os.environ,
|
|
{"CI_GITEA_TOKEN": "tok123", "CI_GITEA_USERNAME": "emil"},
|
|
clear=True,
|
|
):
|
|
_install_in_image(str(venv_link), "/opt/venv", "lint", "git.host", "org")
|
|
|
|
env = mock_run.call_args[1]["env"]
|
|
assert "PIP_EXTRA_INDEX_URL" in env
|
|
assert "emil:tok123@git.host" in env["PIP_EXTRA_INDEX_URL"]
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_install_with_token_defaults_username(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_run: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(os.environ, {"CI_GITEA_TOKEN": "tok123"}, clear=True):
|
|
_install_in_image(str(venv_link), "/opt/venv", "", "host", "org")
|
|
|
|
env = mock_run.call_args[1]["env"]
|
|
assert "emil:tok123@host" in env["PIP_EXTRA_INDEX_URL"]
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_install_removes_existing_link(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_run: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = True
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.unlink = MagicMock()
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
_install_in_image(str(venv_link), "/opt/venv", "", "host", "org")
|
|
|
|
mock_path.return_value.unlink.assert_called_once()
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_install_removes_existing_symlink(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_run: MagicMock,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = True
|
|
mock_path.return_value.unlink = MagicMock()
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
_install_in_image(str(venv_link), "/opt/venv", "", "host", "org")
|
|
|
|
mock_path.return_value.unlink.assert_called_once()
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_install_failure_raises(self, mock_path: MagicMock, mock_run: MagicMock, tmp_path: Path) -> None:
|
|
venv_link = tmp_path / ".venv"
|
|
mock_path.return_value.exists.return_value = False
|
|
mock_path.return_value.is_symlink.return_value = False
|
|
mock_path.return_value.symlink_to = MagicMock()
|
|
mock_run.side_effect = subprocess.CalledProcessError(1, ["pip"])
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
with pytest.raises(subprocess.CalledProcessError):
|
|
_install_in_image(str(venv_link), "/opt/venv", "", "host", "org")
|
|
|
|
|
|
class TestFallbackToSetupCi:
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
def test_fallback_runs_make_setup_ci(self, mock_run: MagicMock) -> None:
|
|
_fallback_to_setup_ci()
|
|
mock_run.assert_called_once_with(["make", "setup-ci"], check=True)
|
|
|
|
@patch("devx.tools.setup_image.subprocess.run")
|
|
def test_fallback_failure_raises(self, mock_run: MagicMock) -> None:
|
|
mock_run.side_effect = subprocess.CalledProcessError(1, ["make"])
|
|
with pytest.raises(subprocess.CalledProcessError):
|
|
_fallback_to_setup_ci()
|
|
|
|
|
|
class TestCli:
|
|
@patch("devx.tools.setup_image._fallback_to_setup_ci")
|
|
@patch("devx.tools.setup_image._install_in_image")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_cli_with_opt_venv_present(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_install: MagicMock,
|
|
mock_fallback: MagicMock,
|
|
) -> None:
|
|
mock_path.return_value.is_dir.return_value = True
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["--extras", "ci,lint"])
|
|
assert result.exit_code == 0
|
|
mock_install.assert_called_once()
|
|
|
|
@patch("devx.tools.setup_image._install_in_image")
|
|
@patch("devx.tools.setup_image._fallback_to_setup_ci")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_cli_falls_back_when_no_opt_venv(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_fallback: MagicMock,
|
|
mock_install: MagicMock,
|
|
) -> None:
|
|
mock_path.return_value.is_dir.return_value = False
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code == 0
|
|
mock_fallback.assert_called_once()
|
|
|
|
@patch("devx.tools.setup_image._fallback_to_setup_ci")
|
|
@patch("devx.tools.setup_image._install_in_image")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_cli_default_values(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_install: MagicMock,
|
|
mock_fallback: MagicMock,
|
|
) -> None:
|
|
mock_path.return_value.is_dir.return_value = True
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code == 0
|
|
call_args = mock_install.call_args[0]
|
|
assert call_args[0] == ".venv"
|
|
assert call_args[1] == "/opt/venv"
|
|
assert call_args[2] == "" # no extras
|
|
assert call_args[3] == "git.oblachno.oblachno.fyi"
|
|
assert call_args[4] == "oblachno-oss"
|
|
|
|
@patch("devx.tools.setup_image._fallback_to_setup_ci")
|
|
@patch("devx.tools.setup_image._install_in_image")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_cli_custom_venv_and_gitea(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_install: MagicMock,
|
|
mock_fallback: MagicMock,
|
|
) -> None:
|
|
mock_path.return_value.is_dir.return_value = True
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
["--venv", ".custom-venv", "--gitea-host", "gitea.io", "--gitea-org", "myorg"],
|
|
)
|
|
assert result.exit_code == 0
|
|
call_args = mock_install.call_args[0]
|
|
assert call_args[0] == ".custom-venv"
|
|
assert call_args[3] == "gitea.io"
|
|
assert call_args[4] == "myorg"
|
|
|
|
@patch("devx.tools.setup_image._fallback_to_setup_ci")
|
|
@patch("devx.tools.setup_image._install_in_image")
|
|
@patch("devx.tools.setup_image.Path")
|
|
def test_cli_with_extras(
|
|
self,
|
|
mock_path: MagicMock,
|
|
mock_install: MagicMock,
|
|
mock_fallback: MagicMock,
|
|
) -> None:
|
|
mock_path.return_value.is_dir.return_value = True
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["--extras", "lint"])
|
|
assert result.exit_code == 0
|
|
assert mock_install.call_args[0][2] == "lint"
|