mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-20 17:50:12 +02:00
Fix virtual printer config changes ignored on running instances
sync_from_db() skipped VPs already in self._instances without checking if their config had changed. Mode, model, access code, bind IP, remote interface IP, and target printer changes were silently ignored until manual toggle off/on or full restart. Now detects config drift and restarts affected instances.
This commit is contained in:
@@ -310,6 +310,192 @@ class TestVirtualPrinterManager:
|
||||
await manager.stop_all()
|
||||
assert len(manager._instances) == 0
|
||||
|
||||
# ========================================================================
|
||||
# Tests for sync_from_db config change detection
|
||||
# ========================================================================
|
||||
|
||||
def _make_db_vp(self, **overrides):
|
||||
"""Create a mock VirtualPrinter DB object."""
|
||||
defaults = {
|
||||
"id": 1,
|
||||
"name": "TestVP",
|
||||
"enabled": True,
|
||||
"mode": "immediate",
|
||||
"model": "C11",
|
||||
"access_code": "12345678",
|
||||
"serial_suffix": "391800001",
|
||||
"bind_ip": "",
|
||||
"remote_interface_ip": "",
|
||||
"target_printer_id": None,
|
||||
"position": 0,
|
||||
}
|
||||
defaults.update(overrides)
|
||||
vp = MagicMock()
|
||||
for k, v in defaults.items():
|
||||
setattr(vp, k, v)
|
||||
return vp
|
||||
|
||||
def _setup_sync_mocks(self, manager, enabled_vps, tmp_path):
|
||||
"""Wire up session_factory mock for sync_from_db."""
|
||||
mock_result = MagicMock()
|
||||
mock_result.scalars.return_value.all.return_value = enabled_vps
|
||||
|
||||
mock_db = AsyncMock()
|
||||
mock_db.execute = AsyncMock(return_value=mock_result)
|
||||
mock_db.__aenter__ = AsyncMock(return_value=mock_db)
|
||||
mock_db.__aexit__ = AsyncMock(return_value=False)
|
||||
|
||||
manager._session_factory = MagicMock(return_value=mock_db)
|
||||
manager._base_dir = tmp_path
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_from_db_restarts_on_mode_change(self, manager, tmp_path):
|
||||
"""Verify sync_from_db restarts VP when mode changes."""
|
||||
from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
|
||||
|
||||
inst = VirtualPrinterInstance(
|
||||
vp_id=1,
|
||||
name="TestVP",
|
||||
mode="immediate",
|
||||
model="C11",
|
||||
access_code="12345678",
|
||||
serial_suffix="391800001",
|
||||
base_dir=tmp_path,
|
||||
)
|
||||
inst.stop_server = AsyncMock()
|
||||
manager._instances[1] = inst
|
||||
|
||||
# DB says mode changed to "archive"
|
||||
db_vp = self._make_db_vp(mode="archive")
|
||||
self._setup_sync_mocks(manager, [db_vp], tmp_path)
|
||||
|
||||
with patch.object(manager, "remove_instance", new_callable=AsyncMock) as mock_remove:
|
||||
# Patch VirtualPrinterInstance to prevent actual start
|
||||
with patch("backend.app.services.virtual_printer.manager.VirtualPrinterInstance") as MockInst:
|
||||
mock_new = MagicMock()
|
||||
mock_new.start_server = AsyncMock()
|
||||
MockInst.return_value = mock_new
|
||||
|
||||
await manager.sync_from_db()
|
||||
|
||||
mock_remove.assert_called_once_with(1)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_from_db_restarts_on_access_code_change(self, manager, tmp_path):
|
||||
"""Verify sync_from_db restarts VP when access_code changes."""
|
||||
from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
|
||||
|
||||
inst = VirtualPrinterInstance(
|
||||
vp_id=1,
|
||||
name="TestVP",
|
||||
mode="immediate",
|
||||
model="C11",
|
||||
access_code="12345678",
|
||||
serial_suffix="391800001",
|
||||
base_dir=tmp_path,
|
||||
)
|
||||
inst.stop_server = AsyncMock()
|
||||
manager._instances[1] = inst
|
||||
|
||||
db_vp = self._make_db_vp(access_code="newcode99")
|
||||
self._setup_sync_mocks(manager, [db_vp], tmp_path)
|
||||
|
||||
with patch.object(manager, "remove_instance", new_callable=AsyncMock) as mock_remove:
|
||||
with patch("backend.app.services.virtual_printer.manager.VirtualPrinterInstance") as MockInst:
|
||||
mock_new = MagicMock()
|
||||
mock_new.start_server = AsyncMock()
|
||||
MockInst.return_value = mock_new
|
||||
|
||||
await manager.sync_from_db()
|
||||
|
||||
mock_remove.assert_called_once_with(1)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_from_db_skips_unchanged_instance(self, manager, tmp_path):
|
||||
"""Verify sync_from_db does NOT restart when config is identical."""
|
||||
from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
|
||||
|
||||
inst = VirtualPrinterInstance(
|
||||
vp_id=1,
|
||||
name="TestVP",
|
||||
mode="immediate",
|
||||
model="C11",
|
||||
access_code="12345678",
|
||||
serial_suffix="391800001",
|
||||
base_dir=tmp_path,
|
||||
)
|
||||
manager._instances[1] = inst
|
||||
|
||||
# DB matches running config exactly
|
||||
db_vp = self._make_db_vp()
|
||||
self._setup_sync_mocks(manager, [db_vp], tmp_path)
|
||||
|
||||
with patch.object(manager, "remove_instance", new_callable=AsyncMock) as mock_remove:
|
||||
await manager.sync_from_db()
|
||||
|
||||
mock_remove.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_from_db_restarts_on_bind_ip_change(self, manager, tmp_path):
|
||||
"""Verify sync_from_db restarts VP when bind_ip changes."""
|
||||
from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
|
||||
|
||||
inst = VirtualPrinterInstance(
|
||||
vp_id=1,
|
||||
name="TestVP",
|
||||
mode="immediate",
|
||||
model="C11",
|
||||
access_code="12345678",
|
||||
serial_suffix="391800001",
|
||||
bind_ip="192.168.1.10",
|
||||
base_dir=tmp_path,
|
||||
)
|
||||
inst.stop_server = AsyncMock()
|
||||
manager._instances[1] = inst
|
||||
|
||||
db_vp = self._make_db_vp(bind_ip="192.168.1.20")
|
||||
self._setup_sync_mocks(manager, [db_vp], tmp_path)
|
||||
|
||||
with patch.object(manager, "remove_instance", new_callable=AsyncMock) as mock_remove:
|
||||
with patch("backend.app.services.virtual_printer.manager.VirtualPrinterInstance") as MockInst:
|
||||
mock_new = MagicMock()
|
||||
mock_new.start_server = AsyncMock()
|
||||
MockInst.return_value = mock_new
|
||||
|
||||
await manager.sync_from_db()
|
||||
|
||||
mock_remove.assert_called_once_with(1)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_from_db_restarts_on_model_change(self, manager, tmp_path):
|
||||
"""Verify sync_from_db restarts VP when model changes."""
|
||||
from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
|
||||
|
||||
inst = VirtualPrinterInstance(
|
||||
vp_id=1,
|
||||
name="TestVP",
|
||||
mode="immediate",
|
||||
model="C11",
|
||||
access_code="12345678",
|
||||
serial_suffix="391800001",
|
||||
base_dir=tmp_path,
|
||||
)
|
||||
inst.stop_server = AsyncMock()
|
||||
manager._instances[1] = inst
|
||||
|
||||
db_vp = self._make_db_vp(model="C12")
|
||||
self._setup_sync_mocks(manager, [db_vp], tmp_path)
|
||||
|
||||
with patch.object(manager, "remove_instance", new_callable=AsyncMock) as mock_remove:
|
||||
with patch("backend.app.services.virtual_printer.manager.VirtualPrinterInstance") as MockInst:
|
||||
mock_new = MagicMock()
|
||||
mock_new.start_server = AsyncMock()
|
||||
MockInst.return_value = mock_new
|
||||
|
||||
await manager.sync_from_db()
|
||||
|
||||
mock_remove.assert_called_once_with(1)
|
||||
|
||||
|
||||
class TestFTPSession:
|
||||
"""Tests for FTP session handling."""
|
||||
|
||||
Reference in New Issue
Block a user