#!/usr/bin/env python3 """ Tests for the systemd unit file parser and validator. """ from backend.app.core.unit_file import SystemdUnitFile, UnitType, create_unit_file class TestSystemdUnitFile: """Test cases for the SystemdUnitFile class.""" def test_parse_simple_service(self): """Test parsing a simple service unit file.""" content = """[Unit] Description=Test Service After=network.target [Service] Type=simple ExecStart=/usr/bin/test User=testuser Restart=on-failure [Install] WantedBy=multi-user.target """ unit = SystemdUnitFile(content=content) assert unit.get_unit_type() == UnitType.SERVICE info = unit.get_info() assert info.description == "Test Service" assert info.unit_type == UnitType.SERVICE def test_validate_valid_service(self): """Test validation of a valid service unit file.""" content = """[Unit] Description=Valid Service [Service] Type=simple ExecStart=/usr/bin/valid-service [Install] WantedBy=multi-user.target """ unit = SystemdUnitFile(content=content) errors = unit.validate() # Should have no errors error_list = [e for e in errors if e.severity == "error"] assert len(error_list) == 0 def test_validate_invalid_service_type(self): """Test validation catches invalid service type.""" content = """[Unit] Description=Invalid Service [Service] Type=invalid-type ExecStart=/usr/bin/test [Install] WantedBy=multi-user.target """ unit = SystemdUnitFile(content=content) errors = unit.validate() # Should have an error about invalid service type error_list = [e for e in errors if e.severity == "error"] assert len(error_list) > 0 assert any("Invalid service type" in e.message for e in error_list) def test_validate_missing_exec_start(self): """Test validation catches missing ExecStart for simple service.""" content = """[Unit] Description=Missing ExecStart [Service] Type=simple User=testuser [Install] WantedBy=multi-user.target """ unit = SystemdUnitFile(content=content) errors = unit.validate() # Should have an error about missing ExecStart error_list = [e for e in errors if e.severity == "error"] assert len(error_list) > 0 assert any("ExecStart" in e.message for e in error_list) def test_parse_timer_unit(self): """Test parsing a timer unit file.""" content = """[Unit] Description=Test Timer [Timer] OnCalendar=daily Persistent=true [Install] WantedBy=timers.target """ unit = SystemdUnitFile(content=content) assert unit.get_unit_type() == UnitType.TIMER def test_validate_timer_missing_schedule(self): """Test validation catches timer without schedule.""" content = """[Unit] Description=Timer without schedule [Timer] Persistent=true [Install] WantedBy=timers.target """ unit = SystemdUnitFile(content=content) errors = unit.validate() # Should have an error about missing timer specification error_list = [e for e in errors if e.severity == "error"] assert len(error_list) > 0 assert any("timing specification" in e.message.lower() for e in error_list) def test_parse_socket_unit(self): """Test parsing a socket unit file.""" content = """[Unit] Description=Test Socket [Socket] ListenStream=127.0.0.1:8080 SocketUser=www-data [Install] WantedBy=sockets.target """ unit = SystemdUnitFile(content=content) assert unit.get_unit_type() == UnitType.SOCKET def test_validate_socket_missing_listen(self): """Test validation catches socket without listen specification.""" content = """[Unit] Description=Socket without listen [Socket] SocketUser=www-data [Install] WantedBy=sockets.target """ unit = SystemdUnitFile(content=content) errors = unit.validate() # Should have an error about missing listen specification error_list = [e for e in errors if e.severity == "error"] assert len(error_list) > 0 assert any("Listen specification" in e.message for e in error_list) def test_set_and_get_values(self): """Test setting and getting values in unit file.""" unit = SystemdUnitFile() unit.set_value("Unit", "Description", "Test Description") unit.set_value("Service", "Type", "simple") unit.set_value("Service", "ExecStart", "/usr/bin/test") assert unit.get_value("Unit", "Description") == "Test Description" assert unit.get_value("Service", "Type") == "simple" assert unit.get_value("Service", "ExecStart") == "/usr/bin/test" def test_remove_key(self): """Test removing keys from unit file.""" unit = SystemdUnitFile() unit.set_value("Service", "User", "testuser") assert unit.get_value("Service", "User") == "testuser" removed = unit.remove_key("Service", "User") assert removed is True assert unit.get_value("Service", "User") is None def test_to_string(self): """Test converting unit file back to string format.""" unit = SystemdUnitFile() unit.set_value("Unit", "Description", "Test Service") unit.set_value("Service", "Type", "simple") unit.set_value("Service", "ExecStart", "/usr/bin/test") unit.set_value("Install", "WantedBy", "multi-user.target") content = unit.to_string() assert "[Unit]" in content assert "Description=Test Service" in content assert "[Service]" in content assert "Type=simple" in content assert "ExecStart=/usr/bin/test" in content assert "[Install]" in content assert "WantedBy=multi-user.target" in content def test_validate_time_span(self): """Test time span validation.""" unit = SystemdUnitFile() # Valid time spans valid_content = """[Service] TimeoutStartSec=30s RestartSec=5min """ unit = SystemdUnitFile(content=valid_content) errors = unit.validate() time_errors = [e for e in errors if "time span" in e.message.lower()] assert len(time_errors) == 0 # Invalid time span invalid_content = """[Service] TimeoutStartSec=invalid-time """ unit = SystemdUnitFile(content=invalid_content) errors = unit.validate() time_errors = [e for e in errors if "time span" in e.message.lower()] assert len(time_errors) > 0 class TestCreateUnitFile: """Test cases for the create_unit_file function.""" def test_create_simple_service(self): """Test creating a simple service unit file.""" unit = create_unit_file( UnitType.SERVICE, description="Test Service", exec_start="/usr/bin/test", user="testuser", restart="on-failure", ) assert unit.get_unit_type() == UnitType.SERVICE assert unit.get_value("Unit", "Description") == "Test Service" assert unit.get_value("Service", "ExecStart") == "/usr/bin/test" assert unit.get_value("Service", "User") == "testuser" assert unit.get_value("Service", "Restart") == "on-failure" def test_create_timer_unit(self): """Test creating a timer unit file.""" unit = create_unit_file( UnitType.TIMER, description="Test Timer", on_calendar="daily", persistent=True, ) assert unit.get_unit_type() == UnitType.TIMER assert unit.get_value("Unit", "Description") == "Test Timer" assert unit.get_value("Timer", "OnCalendar") == "daily" assert unit.get_value("Timer", "Persistent") == "true" def test_create_socket_unit(self): """Test creating a socket unit file.""" unit = create_unit_file( UnitType.SOCKET, description="Test Socket", listen_stream="127.0.0.1:8080" ) assert unit.get_unit_type() == UnitType.SOCKET assert unit.get_value("Unit", "Description") == "Test Socket" assert unit.get_value("Socket", "ListenStream") == "127.0.0.1:8080" def test_create_mount_unit(self): """Test creating a mount unit file.""" unit = create_unit_file( UnitType.MOUNT, description="Test Mount", what="/dev/sdb1", where="/mnt/data", type="ext4", ) assert unit.get_unit_type() == UnitType.MOUNT assert unit.get_value("Unit", "Description") == "Test Mount" assert unit.get_value("Mount", "What") == "/dev/sdb1" assert unit.get_value("Mount", "Where") == "/mnt/data" assert unit.get_value("Mount", "Type") == "ext4" def test_create_with_dependencies(self): """Test creating unit file with dependencies.""" unit = create_unit_file( UnitType.SERVICE, description="Service with dependencies", exec_start="/usr/bin/test", requires=["database.service"], wants=["network.service"], after=["network.target", "database.service"], ) assert unit.get_value("Unit", "Requires") == "database.service" assert unit.get_value("Unit", "Wants") == "network.service" assert unit.get_value("Unit", "After") == "network.target database.service"