151 lines
5.3 KiB
Python
151 lines
5.3 KiB
Python
# =================== AIPass ====================
|
|
# Name: test_timer_install.py
|
|
# Description: Tests for the timer_install module (systemd user timer installer)
|
|
# Version: 1.0.0
|
|
# Created: 2026-06-25
|
|
# Modified: 2026-06-25
|
|
# =============================================
|
|
|
|
"""Tests for the timer_install module (systemd user timer installer)."""
|
|
|
|
import subprocess
|
|
from unittest.mock import patch, MagicMock
|
|
from pathlib import Path
|
|
|
|
from aipass.daemon.apps.modules.timer_install import (
|
|
handle_command,
|
|
HANDLED_COMMANDS,
|
|
_run_systemctl,
|
|
_install,
|
|
_uninstall,
|
|
)
|
|
|
|
|
|
class TestHandleCommand:
|
|
"""Tests for command routing."""
|
|
|
|
def test_handles_install_timer(self):
|
|
"""Verify install-timer is in handled commands."""
|
|
assert "install-timer" in HANDLED_COMMANDS
|
|
|
|
def test_handles_uninstall_timer(self):
|
|
"""Verify uninstall-timer is in handled commands."""
|
|
assert "uninstall-timer" in HANDLED_COMMANDS
|
|
|
|
def test_rejects_unknown(self):
|
|
"""Unknown commands return False."""
|
|
assert handle_command("unknown", []) is False
|
|
|
|
def test_help_flag(self, capsys):
|
|
"""Help flag prints usage and returns True."""
|
|
result = handle_command("install-timer", ["--help"])
|
|
assert result is True
|
|
|
|
|
|
class TestRunSystemctl:
|
|
"""Tests for the systemctl wrapper."""
|
|
|
|
@patch("subprocess.run")
|
|
def test_success(self, mock_run):
|
|
"""Successful systemctl returns True."""
|
|
mock_run.return_value = MagicMock(returncode=0)
|
|
assert _run_systemctl("status", "daemon-tick.timer") is True
|
|
|
|
@patch("subprocess.run")
|
|
def test_failure_returncode(self, mock_run):
|
|
"""Non-zero returncode returns False."""
|
|
mock_run.return_value = MagicMock(returncode=1, stderr="unit not found")
|
|
assert _run_systemctl("start", "daemon-tick.timer") is False
|
|
|
|
@patch("subprocess.run", side_effect=FileNotFoundError)
|
|
def test_systemctl_not_found(self, mock_run):
|
|
"""Missing systemctl returns False."""
|
|
assert _run_systemctl("status", "daemon-tick.timer") is False
|
|
|
|
@patch(
|
|
"subprocess.run",
|
|
side_effect=subprocess.TimeoutExpired(cmd="systemctl", timeout=15),
|
|
)
|
|
def test_timeout(self, mock_run):
|
|
"""Timed-out systemctl returns False."""
|
|
assert _run_systemctl("status", "daemon-tick.timer") is False
|
|
|
|
|
|
class TestInstall:
|
|
"""Tests for the install flow."""
|
|
|
|
def test_install_missing_unit_file(self):
|
|
"""Returns 1 when unit files are missing."""
|
|
with patch(
|
|
"aipass.daemon.apps.modules.timer_install._DAEMON_ROOT",
|
|
Path("/nonexistent"),
|
|
):
|
|
result = _install()
|
|
assert result == 1
|
|
|
|
@patch("aipass.daemon.apps.modules.timer_install._run_systemctl", return_value=True)
|
|
@patch("shutil.copy2")
|
|
def test_install_success(self, mock_copy, mock_systemctl, tmp_path):
|
|
"""Successful install copies files and calls systemctl 3 times."""
|
|
service = tmp_path / "daemon-tick.service"
|
|
timer = tmp_path / "daemon-tick.timer"
|
|
service.write_text("[Unit]\n")
|
|
timer.write_text("[Unit]\n")
|
|
|
|
install_dir = tmp_path / "systemd"
|
|
install_dir.mkdir()
|
|
|
|
with (
|
|
patch("aipass.daemon.apps.modules.timer_install._DAEMON_ROOT", tmp_path),
|
|
patch("aipass.daemon.apps.modules.timer_install._UNIT_DIR", install_dir),
|
|
):
|
|
result = _install()
|
|
assert result == 0
|
|
assert mock_systemctl.call_count == 3
|
|
|
|
@patch("aipass.daemon.apps.modules.timer_install._run_systemctl")
|
|
@patch("shutil.copy2")
|
|
def test_install_systemctl_fails(self, mock_copy, mock_systemctl, tmp_path):
|
|
"""Returns 1 when systemctl fails."""
|
|
service = tmp_path / "daemon-tick.service"
|
|
timer = tmp_path / "daemon-tick.timer"
|
|
service.write_text("[Unit]\n")
|
|
timer.write_text("[Unit]\n")
|
|
|
|
install_dir = tmp_path / "systemd"
|
|
install_dir.mkdir()
|
|
|
|
mock_systemctl.return_value = False
|
|
|
|
with (
|
|
patch("aipass.daemon.apps.modules.timer_install._DAEMON_ROOT", tmp_path),
|
|
patch("aipass.daemon.apps.modules.timer_install._UNIT_DIR", install_dir),
|
|
):
|
|
result = _install()
|
|
assert result == 1
|
|
|
|
|
|
class TestUninstall:
|
|
"""Tests for the uninstall flow."""
|
|
|
|
@patch("aipass.daemon.apps.modules.timer_install._run_systemctl", return_value=True)
|
|
def test_uninstall_files_not_present(self, mock_systemctl, tmp_path):
|
|
"""Returns 0 even when unit files are already absent."""
|
|
with patch("aipass.daemon.apps.modules.timer_install._UNIT_DIR", tmp_path):
|
|
result = _uninstall()
|
|
assert result == 0
|
|
|
|
@patch("aipass.daemon.apps.modules.timer_install._run_systemctl", return_value=True)
|
|
def test_uninstall_removes_files(self, mock_systemctl, tmp_path):
|
|
"""Removes unit files from the target directory."""
|
|
service = tmp_path / "daemon-tick.service"
|
|
timer = tmp_path / "daemon-tick.timer"
|
|
service.write_text("[Unit]\n")
|
|
timer.write_text("[Unit]\n")
|
|
|
|
with patch("aipass.daemon.apps.modules.timer_install._UNIT_DIR", tmp_path):
|
|
result = _uninstall()
|
|
assert result == 0
|
|
assert not service.exists()
|
|
assert not timer.exists()
|