Files
AstrBot/tests/cli/test_uninstall.py
LIghtJUNction 976398d1f2 feat(cli): enhance backup capabilities and refactor path management
- Introduce 'astrbot bk' command with GPG signing, encryption, and digest support
- Add import/export functionality using core backup modules
- Refactor path management to use 'AstrbotPaths' singleton across CLI commands
- Replace blocking subprocess calls with asyncio.create_subprocess_exec in backup command
- Add comprehensive tests for uninstall and backup commands
- Improve module resource handling for bundled dashboard assets
2026-03-17 18:32:32 +08:00

139 lines
4.4 KiB
Python

import platform
import shutil
import subprocess
from pathlib import Path
from unittest.mock import MagicMock, call, patch
import pytest
from click.testing import CliRunner
from astrbot.cli.commands.cmd_uninstall import uninstall
@pytest.fixture
def mock_systemctl():
"""Mock shutil.which('systemctl') and subprocess.run"""
with patch("astrbot.cli.commands.cmd_uninstall.shutil.which") as mock_which, patch(
"astrbot.cli.commands.cmd_uninstall.subprocess.run"
) as mock_run:
mock_which.return_value = "/usr/bin/systemctl"
yield mock_which, mock_run
@pytest.fixture
def mock_astrbot_paths(tmp_path):
"""Mock astrbot_paths to use a temporary directory"""
with patch("astrbot.cli.commands.cmd_uninstall.astrbot_paths") as mock_paths:
# Create a fake astrbot root structure in tmp_path
root = tmp_path / "astrbot_root"
root.mkdir()
data = root / "data"
data.mkdir()
dot_astrbot = root / ".astrbot"
dot_astrbot.touch()
lock_file = root / "astrbot.lock"
lock_file.touch()
mock_paths.root = root
mock_paths.data = data
yield mock_paths
@pytest.mark.skipif(platform.system() != "Linux", reason="Systemd tests only on Linux")
def test_uninstall_systemd_service(mock_systemctl, mock_astrbot_paths):
"""Test systemd service removal"""
mock_which, mock_run = mock_systemctl
runner = CliRunner()
# Mock Path.home() to return a temp directory
with patch("pathlib.Path.home") as mock_home:
fake_home = mock_astrbot_paths.root / "home"
fake_home.mkdir()
mock_home.return_value = fake_home
# Create fake service file
service_dir = fake_home / ".config" / "systemd" / "user"
service_dir.mkdir(parents=True)
service_file = service_dir / "astrbot.service"
service_file.write_text("fake service content")
# Run with --yes to skip confirmation, --keep-data to focus on systemd
result = runner.invoke(uninstall, ["--yes", "--keep-data"])
assert result.exit_code == 0
assert "Stopping AstrBot service..." in result.output
assert "Systemd service uninstalled." in result.output
# Verify subprocess calls
mock_run.assert_any_call(
["systemctl", "--user", "stop", "astrbot"], check=False
)
mock_run.assert_any_call(
["systemctl", "--user", "disable", "astrbot"], check=False
)
mock_run.assert_any_call(["systemctl", "--user", "daemon-reload"], check=True)
# Verify file removed
assert not service_file.exists()
def test_uninstall_data_removal(mock_astrbot_paths):
"""Test data directory removal"""
runner = CliRunner()
# Verify pre-state
assert mock_astrbot_paths.data.exists()
assert (mock_astrbot_paths.root / ".astrbot").exists()
assert (mock_astrbot_paths.root / "astrbot.lock").exists()
# Run uninstall with --yes
result = runner.invoke(uninstall, ["--yes"])
assert result.exit_code == 0
assert "AstrBot data removed successfully" in result.output
# Verify removal
assert not mock_astrbot_paths.data.exists()
assert not (mock_astrbot_paths.root / ".astrbot").exists()
assert not (mock_astrbot_paths.root / "astrbot.lock").exists()
def test_uninstall_keep_data(mock_astrbot_paths):
"""Test uninstall with --keep-data"""
runner = CliRunner()
result = runner.invoke(uninstall, ["--yes", "--keep-data"])
assert result.exit_code == 0
assert "Skipping data removal as requested" in result.output
# Verify data still exists
assert mock_astrbot_paths.data.exists()
def test_uninstall_abort_on_no_confirm(mock_astrbot_paths):
"""Test abort when user declines confirmation"""
runner = CliRunner()
# Input "n" to decline
result = runner.invoke(uninstall, input="n\n")
assert result.exit_code != 0
assert "Aborted" in result.output
# Verify data still exists
assert mock_astrbot_paths.data.exists()
def test_uninstall_not_astrbot_root(mock_astrbot_paths):
"""Test running uninstall in a non-AstrBot directory"""
# Remove the marker files
shutil.rmtree(mock_astrbot_paths.data)
(mock_astrbot_paths.root / ".astrbot").unlink()
runner = CliRunner()
result = runner.invoke(uninstall, ["--yes"])
assert result.exit_code == 0
assert "No AstrBot initialization found" in result.output