return to the installed applications list after each operation

This commit is contained in:
MacRimi
2026-10-15 19:05:19 +02:00
parent 1ca028481d
commit abbb3afc0c
2 changed files with 66 additions and 22 deletions
@@ -7,7 +7,7 @@ from pathlib import Path
import subprocess import subprocess
from types import SimpleNamespace from types import SimpleNamespace
from unittest import TestCase from unittest import TestCase
from unittest.mock import Mock from unittest.mock import Mock, patch
ROOT = Path(__file__).resolve().parents[3] ROOT = Path(__file__).resolve().parents[3]
MENU = ROOT / 'oci/src/proxmenux_oci/management.py' MENU = ROOT / 'oci/src/proxmenux_oci/management.py'
@@ -48,6 +48,42 @@ class SelectionSetupWording(TestCase):
ui.message.assert_called_once_with(EMPTY, 'OCI management') ui.message.assert_called_once_with(EMPTY, 'OCI management')
ui.choose.assert_not_called() ui.choose.assert_not_called()
def _menu(self, inventories, selections):
rows = iter(inventories)
ui = SimpleNamespace(message=Mock(), choose=Mock(side_effect=selections))
refresh = Mock(return_value={})
manage = Mock(return_value=True)
fn = extracted('_interactive_management', os=SimpleNamespace(geteuid=lambda: 0),
shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: next(rows),
_clean_orphans=lambda project: None,
recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None,
carry_records=refresh, manage_instance=manage, menu_size=lambda: 10,
STATUS_LABELS={}, translate=lambda s: s)
fn(Path('/fixture'), ui)
return ui, refresh, manage
def test_the_list_comes_back_after_an_operation_without_refreshing_every_copy(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
ui, refresh, manage = self._menu([[row], [row]], ['101', None])
self.assertEqual(ui.choose.call_count, 2)
manage.assert_called_once()
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, shown=True)
def test_removing_the_last_application_returns_without_a_message(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
ui, _, manage = self._menu([[row], []], ['101'])
manage.assert_called_once()
ui.message.assert_not_called()
def test_the_watchdog_refreshes_the_copy_of_the_containers_it_changed(self):
refresh = Mock(return_value={})
watchdog = SimpleNamespace(set_watchdog=Mock(side_effect=[[101, 102]]))
fn = extracted('set_watchdog', sys=SimpleNamespace(path=[]), carry_records=refresh,
instances=SimpleNamespace(ROOT=Path('/fixture')))
with patch.dict('sys.modules', {'oci_watchdog': watchdog}):
self.assertTrue(fn(Path('/fixture'), [102], True))
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, vmids=[101, 102])
def test_selector_filters_missing_guest_without_erasing_saved_contract(self): def test_selector_filters_missing_guest_without_erasing_saved_contract(self):
class Entry: class Entry:
name = '101' name = '101'
+8
View File
@@ -254,10 +254,16 @@ def _interactive_management(project, ui):
recover_automatically(project) recover_automatically(project)
offer_recovery(project, ui) offer_recovery(project, ui)
carry_records(project, mount_stopped=False, shown=True) carry_records(project, mount_stopped=False, shown=True)
# After each operation the list is shown again. Every operation leaves
# the copies of the records up to date, so they are not all refreshed again.
first = True
while True:
rows = saved_inventory(project) rows = saved_inventory(project)
if not rows: if not rows:
if first:
ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management')) ui.message(translate('No registered OCI containers are available for selection on this host.'), translate('OCI management'))
return return
first = False
# Same layout as the catalog lists; only an unusual state is shown. # Same layout as the catalog lists; only an unusual state is shown.
tag_width = max(len(str(r['vmid'])) for r in rows) tag_width = max(len(str(r['vmid'])) for r in rows)
header = f" {'CT':<{tag_width + 2}}{translate('Application')[:32]:<32} {translate('Image')}" header = f" {'CT':<{tag_width + 2}}{translate('Application')[:32]:<32} {translate('Image')}"
@@ -291,6 +297,8 @@ def set_watchdog(project, vmids, enabled):
done.update(oci_watchdog.set_watchdog(instances.ROOT, vmid, enabled)) done.update(oci_watchdog.set_watchdog(instances.ROOT, vmid, enabled))
except (OSError, ValueError, KeyError): except (OSError, ValueError, KeyError):
return False return False
# The copy a restore on another host reads carries the watchdog as well.
carry_records(project, mount_stopped=False, vmids=sorted(done))
return True return True