test(services): add unit tests for backup and message services

This commit is contained in:
dw-0
2026-07-11 17:49:51 +02:00
parent 9b93450d98
commit 477d737489
3 changed files with 274 additions and 0 deletions
@@ -0,0 +1,213 @@
from __future__ import annotations
from pathlib import Path
import pytest
from core.services.backup_service import BackupService
class FakeKlipper:
def __init__(self, suffix: str = "") -> None:
self.suffix = suffix
self.data_dir = Path(f"/tmp/klipper{suffix}_data")
self.cfg_file = self.data_dir.joinpath("printer.cfg")
class FakeMoonraker:
def __init__(self, suffix: str = "") -> None:
self.suffix = suffix
self.data_dir = Path(f"/tmp/moonraker{suffix}_data")
self.cfg_file = self.data_dir.joinpath("moonraker.conf")
@pytest.fixture
def service(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> BackupService:
monkeypatch.setattr(Path, "home", lambda: tmp_path)
svc = BackupService()
return svc
class TestBackupFile:
def test_returns_false_when_source_does_not_exist(self, service: BackupService) -> None:
result = service.backup_file(source_path=Path("/does/not/exist.cfg"))
assert result is False
def test_returns_false_when_source_is_not_a_file(
self, service: BackupService, tmp_path: Path
) -> None:
directory = tmp_path / "directory"
directory.mkdir()
result = service.backup_file(source_path=directory)
assert result is False
def test_creates_backup_and_returns_true(
self, service: BackupService, tmp_path: Path
) -> None:
source = tmp_path / "printer.cfg"
source.write_text("config")
result = service.backup_file(source_path=source)
assert result is True
backups = list(service.backup_root.glob("*.cfg"))
assert len(backups) == 1
assert backups[0].read_text() == "config"
def test_skips_when_target_already_exists(
self, service: BackupService, tmp_path: Path
) -> None:
source = tmp_path / "printer.cfg"
source.write_text("config")
service.backup_root.mkdir(parents=True, exist_ok=True)
expected_name = f"printer_{service.timestamp}.cfg"
service.backup_root.joinpath(expected_name).touch()
result = service.backup_file(source_path=source)
assert result is True
def test_returns_false_on_copy_error(
self, service: BackupService, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
source = tmp_path / "printer.cfg"
source.write_text("config")
monkeypatch.setattr(
"core.services.backup_service.shutil.copy2",
lambda *a, **k: (_ for _ in ()).throw(RuntimeError("copy failed")),
)
result = service.backup_file(source_path=source)
assert result is False
class TestBackupDirectory:
def test_returns_none_when_source_does_not_exist(
self, service: BackupService
) -> None:
result = service.backup_directory(
source_path=Path("/does/not/exist"), backup_name="config"
)
assert result is None
def test_returns_none_when_source_is_not_a_directory(
self, service: BackupService, tmp_path: Path
) -> None:
file_path = tmp_path / "file.txt"
file_path.write_text("data")
result = service.backup_directory(
source_path=file_path, backup_name="config"
)
assert result is None
def test_creates_timestamped_backup_directory(
self, service: BackupService, tmp_path: Path
) -> None:
source = tmp_path / "config"
source.mkdir()
source.joinpath("printer.cfg").write_text("data")
result = service.backup_directory(
source_path=source, backup_name="config"
)
assert result is not None
assert result.exists()
assert result.joinpath("printer.cfg").read_text() == "data"
def test_reuses_existing_backup_and_skips_existing_files(
self, service: BackupService, tmp_path: Path
) -> None:
source = tmp_path / "config"
source.mkdir()
source.joinpath("printer.cfg").write_text("new")
backup_dir = service.backup_root.joinpath(f"config_{service.timestamp}")
backup_dir.mkdir(parents=True)
backup_dir.joinpath("printer.cfg").write_text("old")
result = service.backup_directory(
source_path=source, backup_name="config"
)
assert result == backup_dir
assert result.joinpath("printer.cfg").read_text() == "old"
def test_returns_none_on_copy_error(
self, service: BackupService, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
source = tmp_path / "config"
source.mkdir()
source.joinpath("file.cfg").write_text("data")
monkeypatch.setattr(
"core.services.backup_service.shutil.copytree",
lambda *a, **k: (_ for _ in ()).throw(RuntimeError("copytree failed")),
)
result = service.backup_directory(
source_path=source, backup_name="config"
)
assert result is None
class TestSpecificBackupMethods:
def test_backup_printer_cfg_backs_up_each_instance(
self, service: BackupService, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
instances = [FakeKlipper(""), FakeKlipper("a")]
for i in instances:
i.cfg_file.parent.mkdir(parents=True, exist_ok=True)
i.cfg_file.write_text("printer config")
monkeypatch.setattr(
"core.services.backup_service.get_instances", lambda model: instances
)
service.backup_printer_cfg()
backups = list(service.backup_root.rglob("printer*.cfg"))
assert len(backups) == 2
def test_backup_moonraker_conf_backs_up_each_instance(
self, service: BackupService, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
instances = [FakeMoonraker(""), FakeMoonraker("a")]
for i in instances:
i.cfg_file.parent.mkdir(parents=True, exist_ok=True)
i.cfg_file.write_text("moonraker config")
monkeypatch.setattr(
"core.services.backup_service.get_instances", lambda model: instances
)
service.backup_moonraker_conf()
backups = list(service.backup_root.rglob("moonraker*.conf"))
assert len(backups) == 2
def test_backup_printer_config_dir_falls_back_to_home_dirs(
self, service: BackupService, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(
"core.services.backup_service.get_instances", lambda model: []
)
printer_data = tmp_path / "printer_data"
printer_data.mkdir()
config_dir = printer_data / "config"
config_dir.mkdir()
config_dir.joinpath("printer.cfg").write_text("home config")
service.backup_printer_config_dir()
backups = list(service.backup_root.rglob("printer_data/config_*/printer.cfg"))
assert len(backups) == 1
def test_backup_printer_config_dir_returns_when_no_dirs_found(
self, service: BackupService, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(
"core.services.backup_service.get_instances", lambda model: []
)
# should not raise and should not create backups
service.backup_printer_config_dir()
assert not service.backup_root.exists()
@@ -0,0 +1,61 @@
from __future__ import annotations
from typing import Any, List
import pytest
from core.services.message_service import Message, MessageService
from core.types.color import Color
@pytest.fixture
def reset_message_service(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(MessageService, "_MessageService__cls_instance", None)
class TestMessage:
def test_default_message_is_empty(self) -> None:
msg = Message()
assert msg.title == ""
assert msg.text == []
assert msg.color == Color.WHITE
assert msg.centered is False
class TestMessageService:
def test_singleton_instance(self, reset_message_service) -> None:
a = MessageService()
b = MessageService()
assert a is b
def test_set_and_display_message(
self, reset_message_service, monkeypatch: pytest.MonkeyPatch
) -> None:
calls: List[Any] = []
monkeypatch.setattr(
"core.services.message_service.Logger.print_dialog",
lambda **kwargs: calls.append(kwargs),
)
svc = MessageService()
msg = Message(title="Hello", text=["world"], color=Color.GREEN)
svc.set_message(msg)
svc.display_message()
assert calls[0]["custom_title"] == "Hello"
assert calls[0]["content"] == ["world"]
def test_display_without_message_does_nothing(
self, reset_message_service, monkeypatch: pytest.MonkeyPatch
) -> None:
calls: List[Any] = []
monkeypatch.setattr(
"core.services.message_service.Logger.print_dialog",
lambda **kwargs: calls.append(kwargs),
)
svc = MessageService()
svc.display_message()
# no message set, so print_dialog should not have been invoked
assert calls == []
assert svc._MessageService__message is None