Formatting and Testing
This commit is contained in:
parent
4d839631a4
commit
10858aaec8
|
|
@ -1,5 +1,6 @@
|
|||
import inspect
|
||||
from typing import Any, Callable, override
|
||||
from collections.abc import Callable
|
||||
from typing import Any, override
|
||||
|
||||
from archinstall.default_profiles.profile import GreeterType
|
||||
from archinstall.lib.applications.application_menu import ApplicationMenu
|
||||
|
|
@ -70,25 +71,22 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
- [•] (Yellow) for unconfigured optional items
|
||||
"""
|
||||
if item.read_only or item.key in (SpecialMenuKey.SAVE.value, SpecialMenuKey.INSTALL.value, SpecialMenuKey.ABORT.value):
|
||||
return ""
|
||||
return ''
|
||||
|
||||
if item.key == 'auth_config':
|
||||
auth_config: AuthenticationConfiguration | None = item.value
|
||||
is_auth_valid = (
|
||||
auth_config is not None
|
||||
and (auth_config.root_enc_password is not None or auth_config.has_superuser())
|
||||
)
|
||||
is_auth_valid = auth_config is not None and (auth_config.root_enc_password is not None or auth_config.has_superuser())
|
||||
if is_auth_valid:
|
||||
return "[bold green][✓][/bold green] "
|
||||
return "[bold red][!][/bold red] "
|
||||
return '[bold green][✓][/bold green] '
|
||||
return '[bold red][!][/bold red] '
|
||||
|
||||
# Standard mandatory or configured item check
|
||||
if item.has_value():
|
||||
return "[bold green][✓][/bold green] "
|
||||
return '[bold green][✓][/bold green] '
|
||||
elif item.mandatory:
|
||||
return "[bold red][!][/bold red] "
|
||||
return '[bold red][!][/bold red] '
|
||||
else:
|
||||
return "[bold yellow][•][/bold yellow] "
|
||||
return '[bold yellow][•][/bold yellow] '
|
||||
|
||||
def _update_item_labels(self) -> None:
|
||||
"""
|
||||
|
|
@ -101,9 +99,9 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
if item.key in new_options:
|
||||
base_title = new_options[item.key]
|
||||
prefix = self._get_status_prefix(item)
|
||||
item.text = f"{prefix}{base_title}"
|
||||
item.text = f'{prefix}{base_title}'
|
||||
|
||||
def _wrap_action(self, key: str, action: Callable) -> Callable:
|
||||
def _wrap_action(self, key: str, action: Callable[..., Any]) -> Callable[..., Any]:
|
||||
async def wrapper(*args, **kwargs) -> Any:
|
||||
if inspect.iscoroutinefunction(action):
|
||||
result = await action(*args, **kwargs)
|
||||
|
|
@ -111,15 +109,15 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
result = action(*args, **kwargs)
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
|
||||
|
||||
item = self._item_group.find_by_key(key)
|
||||
if item:
|
||||
item.value = result
|
||||
|
||||
|
||||
self._update_item_labels()
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
return wrapper
|
||||
|
||||
def _get_menu_options(self, wrap_actions: bool = True) -> list[MenuItem]:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
import asyncio
|
||||
import inspect
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
class MockSpecialMenuKey:
|
||||
SAVE = 'save'
|
||||
INSTALL = 'install'
|
||||
ABORT = 'abort'
|
||||
|
||||
|
||||
class MockMenuItem:
|
||||
def __init__(self, key=None, read_only=False, mandatory=False, value=None):
|
||||
self.key = key
|
||||
self.read_only = read_only
|
||||
self.mandatory = mandatory
|
||||
self.value = value
|
||||
|
||||
def has_value(self):
|
||||
return self.value is not None
|
||||
|
||||
|
||||
def isolated_get_status_prefix(item: MockMenuItem) -> str:
|
||||
special_keys = (MockSpecialMenuKey.SAVE, MockSpecialMenuKey.INSTALL, MockSpecialMenuKey.ABORT)
|
||||
|
||||
if item.read_only or item.key in special_keys:
|
||||
return ''
|
||||
|
||||
if item.key == 'auth_config':
|
||||
auth_config = item.value
|
||||
has_root = getattr(auth_config, 'root_enc_password', None) is not None
|
||||
has_super = getattr(auth_config, 'has_superuser', lambda: False)() if auth_config else False
|
||||
|
||||
if auth_config is not None and (has_root or has_super):
|
||||
return '[bold green][✓][/bold green] '
|
||||
return '[bold red][!][/bold red] '
|
||||
|
||||
if item.has_value():
|
||||
return '[bold green][✓][/bold green] '
|
||||
elif item.mandatory:
|
||||
return '[bold red][!][/bold red] '
|
||||
else:
|
||||
return '[bold yellow][•][/bold yellow] '
|
||||
|
||||
|
||||
def isolated_wrap_action(item_dictionary, update_callback, key, action):
|
||||
|
||||
async def wrapper(*args, **kwargs):
|
||||
if inspect.iscoroutinefunction(action):
|
||||
result = await action(*args, **kwargs)
|
||||
else:
|
||||
result = action(*args, **kwargs)
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
|
||||
if key in item_dictionary:
|
||||
item_dictionary[key].value = result
|
||||
|
||||
update_callback()
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class TestPrefixLogic:
|
||||
def test_special_keys_have_no_prefix(self):
|
||||
item = MockMenuItem(key=MockSpecialMenuKey.SAVE)
|
||||
assert isolated_get_status_prefix(item) == ''
|
||||
|
||||
def test_read_only_has_no_prefix(self):
|
||||
item = MockMenuItem(read_only=True)
|
||||
assert isolated_get_status_prefix(item) == ''
|
||||
|
||||
def test_configured_item_has_checkmark(self):
|
||||
item = MockMenuItem(key='hostname', value='archlinux')
|
||||
prefix = isolated_get_status_prefix(item)
|
||||
assert '[✓]' in prefix
|
||||
assert 'green' in prefix
|
||||
|
||||
def test_missing_mandatory_item_has_warning(self):
|
||||
item = MockMenuItem(key='disk_config', mandatory=True, value=None)
|
||||
prefix = isolated_get_status_prefix(item)
|
||||
assert '[!]' in prefix
|
||||
assert 'red' in prefix
|
||||
|
||||
def test_missing_optional_item_has_dot(self):
|
||||
item = MockMenuItem(key='network_config', mandatory=False, value=None)
|
||||
prefix = isolated_get_status_prefix(item)
|
||||
assert '[•]' in prefix
|
||||
assert 'yellow' in prefix
|
||||
|
||||
|
||||
class TestAuthConfigLogic:
|
||||
def test_auth_missing_entirely(self):
|
||||
item = MockMenuItem(key='auth_config', value=None)
|
||||
assert '[!]' in isolated_get_status_prefix(item)
|
||||
|
||||
def test_auth_invalid_state(self):
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.root_enc_password = None
|
||||
mock_auth.has_superuser.return_value = False
|
||||
|
||||
item = MockMenuItem(key='auth_config', value=mock_auth)
|
||||
assert '[!]' in isolated_get_status_prefix(item)
|
||||
|
||||
def test_auth_valid_root(self):
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.root_enc_password = 'hashed'
|
||||
mock_auth.has_superuser.return_value = False
|
||||
|
||||
item = MockMenuItem(key='auth_config', value=mock_auth)
|
||||
assert '[✓]' in isolated_get_status_prefix(item)
|
||||
|
||||
def test_auth_valid_superuser(self):
|
||||
mock_auth = MagicMock()
|
||||
mock_auth.root_enc_password = None
|
||||
mock_auth.has_superuser.return_value = True
|
||||
|
||||
item = MockMenuItem(key='auth_config', value=mock_auth)
|
||||
assert '[✓]' in isolated_get_status_prefix(item)
|
||||
|
||||
|
||||
class TestActionWrapperLogic:
|
||||
def test_async_action_updates_value(self):
|
||||
mock_item = MockMenuItem(key='disk')
|
||||
item_dict = {'disk': mock_item}
|
||||
update_spy = MagicMock()
|
||||
|
||||
async def dummy_async_action():
|
||||
return 'new_disk_layout'
|
||||
|
||||
wrapped = isolated_wrap_action(item_dict, update_spy, 'disk', dummy_async_action)
|
||||
|
||||
result = asyncio.run(wrapped())
|
||||
|
||||
assert result == 'new_disk_layout'
|
||||
assert mock_item.value == 'new_disk_layout'
|
||||
update_spy.assert_called_once()
|
||||
|
||||
def test_sync_action_updates_value(self):
|
||||
mock_item = MockMenuItem(key='hostname')
|
||||
item_dict = {'hostname': mock_item}
|
||||
update_spy = MagicMock()
|
||||
|
||||
def dummy_sync_action():
|
||||
return 'my-pc'
|
||||
|
||||
wrapped = isolated_wrap_action(item_dict, update_spy, 'hostname', dummy_sync_action)
|
||||
|
||||
result = asyncio.run(wrapped())
|
||||
|
||||
assert result == 'my-pc'
|
||||
assert mock_item.value == 'my-pc'
|
||||
update_spy.assert_called_once()
|
||||
Loading…
Reference in New Issue