3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
138 lines
4.8 KiB
Python
138 lines
4.8 KiB
Python
# SPDX-License-Identifier: MIT
|
|
"""Tests for the ``winpodx uninstall`` Python wrapper (#255 consolidation).
|
|
|
|
The wrapper's only job is to locate ``uninstall.sh`` and ``exec`` it.
|
|
Tests verify (1) the candidate paths cover every install topology and
|
|
(2) ``os.execvp`` is invoked with the right argv when a script is found.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from winpodx.cli.uninstall import _candidate_paths, handle_uninstall
|
|
|
|
|
|
class TestCandidatePaths:
|
|
def test_includes_system_share(self):
|
|
paths = _candidate_paths()
|
|
assert Path("/usr/share/winpodx/uninstall.sh") in paths
|
|
assert Path("/usr/local/share/winpodx/uninstall.sh") in paths
|
|
|
|
def test_includes_curl_bundle(self):
|
|
paths = _candidate_paths()
|
|
assert Path.home() / ".local" / "bin" / "winpodx-app" / "uninstall.sh" in paths
|
|
|
|
def test_includes_venv_prefix(self):
|
|
paths = _candidate_paths()
|
|
assert Path(sys.prefix) / "share" / "winpodx" / "uninstall.sh" in paths
|
|
|
|
def test_includes_dev_repo_root(self):
|
|
paths = _candidate_paths()
|
|
# Should include something ending in repo-root / uninstall.sh
|
|
assert any(p.name == "uninstall.sh" for p in paths)
|
|
|
|
|
|
class TestHandleUninstall:
|
|
def test_exec_invoked_when_script_found(self, tmp_path):
|
|
fake_script = tmp_path / "uninstall.sh"
|
|
fake_script.write_text("#!/usr/bin/env bash\nexit 0\n")
|
|
fake_script.chmod(0o755)
|
|
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[fake_script],
|
|
),
|
|
patch("os.execvp") as mock_exec,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=False, yes=False))
|
|
mock_exec.assert_called_once_with("bash", ["bash", str(fake_script)])
|
|
|
|
def test_purge_flag_forwarded(self, tmp_path):
|
|
fake_script = tmp_path / "uninstall.sh"
|
|
fake_script.write_text("#!/usr/bin/env bash\nexit 0\n")
|
|
fake_script.chmod(0o755)
|
|
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[fake_script],
|
|
),
|
|
patch("os.execvp") as mock_exec,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=True, yes=False))
|
|
args = mock_exec.call_args.args[1]
|
|
assert "--purge" in args
|
|
|
|
def test_yes_flag_forwarded(self, tmp_path):
|
|
fake_script = tmp_path / "uninstall.sh"
|
|
fake_script.write_text("#!/usr/bin/env bash\nexit 0\n")
|
|
fake_script.chmod(0o755)
|
|
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[fake_script],
|
|
),
|
|
patch("os.execvp") as mock_exec,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=False, yes=True))
|
|
args = mock_exec.call_args.args[1]
|
|
assert "--yes" in args
|
|
|
|
def test_both_flags_forwarded(self, tmp_path):
|
|
fake_script = tmp_path / "uninstall.sh"
|
|
fake_script.write_text("#!/usr/bin/env bash\nexit 0\n")
|
|
fake_script.chmod(0o755)
|
|
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[fake_script],
|
|
),
|
|
patch("os.execvp") as mock_exec,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=True, yes=True))
|
|
args = mock_exec.call_args.args[1]
|
|
assert "--purge" in args
|
|
assert "--yes" in args
|
|
|
|
def test_first_existing_path_wins(self, tmp_path):
|
|
first = tmp_path / "first.sh"
|
|
second = tmp_path / "second.sh"
|
|
second.write_text("#!/usr/bin/env bash\nexit 0\n")
|
|
second.chmod(0o755)
|
|
# first does NOT exist; should fall through to second
|
|
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[first, second],
|
|
),
|
|
patch("os.execvp") as mock_exec,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=False, yes=False))
|
|
args = mock_exec.call_args.args[1]
|
|
assert str(second) in args
|
|
assert str(first) not in args
|
|
|
|
def test_systemexit_when_no_script_found(self):
|
|
with (
|
|
patch(
|
|
"winpodx.cli.uninstall._candidate_paths",
|
|
return_value=[Path("/nonexistent/path/uninstall.sh")],
|
|
),
|
|
pytest.raises(SystemExit) as exc,
|
|
):
|
|
handle_uninstall(argparse.Namespace(purge=False, yes=False))
|
|
# Error message should mention the search paths so the user can
|
|
# diagnose. And the curl one-liner so they can recover.
|
|
assert "uninstall.sh not found" in str(exc.value)
|
|
assert "curl -fsSL" in str(exc.value)
|