Initial commit - Phase 3/4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael
2026-01-06 13:45:29 -08:00
parent 1da6730735
commit 4f35df1781
323 changed files with 98287 additions and 1195 deletions

View File

@@ -0,0 +1,300 @@
"""Unit tests for SystemService.
Tests the SystemService business logic layer, ensuring:
- System information queries
- Shutdown/restart operations
- Error handling and error codes
- Command execution and output parsing
"""
import pytest
from unittest.mock import AsyncMock, Mock, patch
from app.backend.services.system_service import SystemService
class TestSystemServiceInfo:
"""Test system information queries."""
@pytest.mark.asyncio
async def test_get_info_success(self):
"""Test successful system info retrieval."""
# Arrange
service = SystemService()
# Act
with patch('psutil.cpu_percent', return_value=25.5), \
patch('psutil.virtual_memory') as mock_memory, \
patch('psutil.disk_usage') as mock_disk, \
patch('psutil.cpu_count', return_value=4), \
patch('psutil.boot_time', return_value=1000000000.0), \
patch('psutil.time.time', return_value=1000086400.0), \
patch('platform.node', return_value='dangerous-pi'), \
patch('platform.system', return_value='Linux'), \
patch('platform.release', return_value='6.1.21'):
# Mock memory stats
mock_memory.return_value = Mock(
total=4294967296, # 4GB
used=2147483648, # 2GB
percent=50.0
)
# Mock disk stats
mock_disk.return_value = Mock(
total=32212254720, # 30GB
used=10737418240, # 10GB
percent=33.3
)
result = await service.get_info()
# Assert
assert result.success is True
assert result.data["hostname"] == "dangerous-pi"
assert result.data["platform"] == "Linux"
assert result.data["cpu"]["count"] == 4
assert result.data["cpu"]["percent"] == 25.5
assert result.data["memory"]["total"] == 4294967296
assert result.data["memory"]["used"] == 2147483648
assert result.data["memory"]["percent"] == 50.0
assert result.data["disk"]["total"] == 32212254720
assert result.data["uptime"] == 86400.0 # 1 day
@pytest.mark.asyncio
async def test_get_info_with_temperature(self):
"""Test system info with CPU temperature."""
# Arrange
service = SystemService()
# Act
with patch('psutil.cpu_percent', return_value=50.0), \
patch('psutil.virtual_memory'), \
patch('psutil.disk_usage'), \
patch('psutil.cpu_count', return_value=4), \
patch('psutil.boot_time', return_value=1000000000.0), \
patch('psutil.time.time', return_value=1000000100.0), \
patch('platform.node', return_value='test'), \
patch('platform.system', return_value='Linux'), \
patch('platform.release', return_value='6.1'), \
patch('pathlib.Path.exists', return_value=True), \
patch('pathlib.Path.read_text', return_value='45678'):
# Mock memory and disk
patch('psutil.virtual_memory', return_value=Mock(total=1, used=1, percent=1))
patch('psutil.disk_usage', return_value=Mock(total=1, used=1, percent=1))
result = await service.get_info()
# Assert
assert result.success is True
assert result.data["cpu"]["temperature"] == 45.678
@pytest.mark.asyncio
async def test_get_info_exception_handling(self):
"""Test system info retrieval with exception."""
# Arrange
service = SystemService()
# Act
with patch('psutil.cpu_percent', side_effect=Exception("psutil error")):
result = await service.get_info()
# Assert
assert result.success is False
assert result.error.code == "system_info_error"
assert "psutil error" in result.error.details
class TestSystemServiceShutdown:
"""Test shutdown operations."""
@pytest.mark.asyncio
async def test_shutdown_immediate(self):
"""Test immediate shutdown."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = ""
result = await service.shutdown(delay=0)
# Assert
assert result.success is True
assert "initiated" in result.data["message"].lower()
mock_cmd.assert_called_once_with("sudo shutdown -h now")
@pytest.mark.asyncio
async def test_shutdown_with_delay(self):
"""Test scheduled shutdown."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = ""
result = await service.shutdown(delay=300) # 5 minutes
# Assert
assert result.success is True
assert "scheduled" in result.data["message"].lower()
assert result.data["delay"] == 300
# 300 seconds = 5 minutes
mock_cmd.assert_called_once_with("sudo shutdown -h +5")
@pytest.mark.asyncio
async def test_shutdown_failure(self):
"""Test shutdown command failure."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.side_effect = RuntimeError("Permission denied")
result = await service.shutdown(delay=0)
# Assert
assert result.success is False
assert result.error.code == "shutdown_error"
assert "Permission denied" in result.error.details
class TestSystemServiceRestart:
"""Test restart operations."""
@pytest.mark.asyncio
async def test_restart_immediate(self):
"""Test immediate restart."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = ""
result = await service.restart(delay=0)
# Assert
assert result.success is True
assert "initiated" in result.data["message"].lower()
mock_cmd.assert_called_once_with("sudo shutdown -r now")
@pytest.mark.asyncio
async def test_restart_with_delay(self):
"""Test scheduled restart."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = ""
result = await service.restart(delay=600) # 10 minutes
# Assert
assert result.success is True
assert "scheduled" in result.data["message"].lower()
assert result.data["delay"] == 600
mock_cmd.assert_called_once_with("sudo shutdown -r +10")
class TestSystemServiceCancelShutdown:
"""Test shutdown cancellation."""
@pytest.mark.asyncio
async def test_cancel_shutdown_success(self):
"""Test successful shutdown cancellation."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = ""
result = await service.cancel_shutdown()
# Assert
assert result.success is True
assert "cancelled" in result.data["message"].lower()
mock_cmd.assert_called_once_with("sudo shutdown -c")
class TestSystemServiceLogs:
"""Test log retrieval."""
@pytest.mark.asyncio
async def test_get_logs_success(self):
"""Test successful log retrieval."""
# Arrange
service = SystemService()
log_content = "Jan 01 12:00:00 test systemd[1]: Started dangerous-pi"
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.return_value = log_content
result = await service.get_logs(service="dangerous-pi", lines=100)
# Assert
assert result.success is True
assert result.data["service"] == "dangerous-pi"
assert result.data["lines"] == 100
assert result.data["logs"] == log_content
mock_cmd.assert_called_once_with(
"sudo journalctl -u dangerous-pi -n 100 --no-pager"
)
@pytest.mark.asyncio
async def test_get_logs_failure(self):
"""Test log retrieval failure."""
# Arrange
service = SystemService()
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd:
mock_cmd.side_effect = RuntimeError("Service not found")
result = await service.get_logs(service="nonexistent")
# Assert
assert result.success is False
assert result.error.code == "logs_error"
class TestSystemServiceStatus:
"""Test service status queries."""
@pytest.mark.asyncio
async def test_get_service_status_active(self):
"""Test getting status of active service."""
# Arrange
service = SystemService()
status_output = "● dangerous-pi.service - Dangerous Pi Backend\n Loaded: loaded\n Active: active (running)"
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd, \
patch.object(service, '_is_service_enabled', new_callable=AsyncMock) as mock_enabled:
mock_cmd.return_value = status_output
mock_enabled.return_value = True
result = await service.get_service_status("dangerous-pi")
# Assert
assert result.success is True
assert result.data["service"] == "dangerous-pi"
assert result.data["active"] is True
assert result.data["enabled"] is True
assert "active (running)" in result.data["status"]
@pytest.mark.asyncio
async def test_get_service_status_inactive(self):
"""Test getting status of inactive service."""
# Arrange
service = SystemService()
status_output = "● test.service\n Loaded: loaded\n Active: inactive (dead)"
# Act
with patch.object(service, '_run_command', new_callable=AsyncMock) as mock_cmd, \
patch.object(service, '_is_service_enabled', new_callable=AsyncMock) as mock_enabled:
mock_cmd.return_value = status_output
mock_enabled.return_value = False
result = await service.get_service_status("test")
# Assert
assert result.success is True
assert result.data["active"] is False
assert result.data["enabled"] is False