Security: - Fix path traversal in _save/_load_last_session by rejecting usernames starting with dot (blocks '..' and hidden file creation) - Add avatar file size limit (10 MB) to prevent DoS via large ~/.face - Add session_name length validation on write (symmetric with read) - Add payload size check to send_message (symmetric with recv_message) - Set log level to INFO in production (was DEBUG) Quality: - Eliminate main-thread blocking on user switch: _cancel_pending_session now sets a cancellation event and closes the socket instead of doing blocking IPC. The login worker checks the event after each step. - Move power actions (reboot/shutdown) to background threads - Catch TimeoutExpired in addition to CalledProcessError for power actions - Consolidate socket cleanup in _login_worker via finally block, remove redundant _close_greetd_sock calls from error callbacks - Fix _select_initial_user to return False for GLib.idle_add deregistration - Fix context manager leak in resolve_wallpaper_path on exception - Pass Config object to GreeterWindow instead of loading it twice
62 lines
1.9 KiB
Python
62 lines
1.9 KiB
Python
# ABOUTME: Tests for power actions — reboot and shutdown via loginctl.
|
|
# ABOUTME: Uses mocking to avoid actually calling system commands.
|
|
|
|
import subprocess
|
|
from unittest.mock import patch, call
|
|
|
|
import pytest
|
|
|
|
from moongreet.power import reboot, shutdown, POWER_TIMEOUT
|
|
|
|
|
|
class TestReboot:
|
|
"""Tests for the reboot power action."""
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_calls_loginctl_reboot(self, mock_run) -> None:
|
|
reboot()
|
|
|
|
mock_run.assert_called_once_with(
|
|
["loginctl", "reboot"], check=True, timeout=POWER_TIMEOUT
|
|
)
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_raises_on_failure(self, mock_run) -> None:
|
|
mock_run.side_effect = subprocess.CalledProcessError(1, "loginctl")
|
|
|
|
with pytest.raises(subprocess.CalledProcessError):
|
|
reboot()
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_raises_on_timeout(self, mock_run) -> None:
|
|
mock_run.side_effect = subprocess.TimeoutExpired("loginctl", POWER_TIMEOUT)
|
|
|
|
with pytest.raises(subprocess.TimeoutExpired):
|
|
reboot()
|
|
|
|
|
|
class TestShutdown:
|
|
"""Tests for the shutdown power action."""
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_calls_loginctl_poweroff(self, mock_run) -> None:
|
|
shutdown()
|
|
|
|
mock_run.assert_called_once_with(
|
|
["loginctl", "poweroff"], check=True, timeout=POWER_TIMEOUT
|
|
)
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_raises_on_failure(self, mock_run) -> None:
|
|
mock_run.side_effect = subprocess.CalledProcessError(1, "loginctl")
|
|
|
|
with pytest.raises(subprocess.CalledProcessError):
|
|
shutdown()
|
|
|
|
@patch("moongreet.power.subprocess.run")
|
|
def test_raises_on_timeout(self, mock_run) -> None:
|
|
mock_run.side_effect = subprocess.TimeoutExpired("loginctl", POWER_TIMEOUT)
|
|
|
|
with pytest.raises(subprocess.TimeoutExpired):
|
|
shutdown()
|