mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 18:54:32 +00:00
Separation of concerns: merge silent/UI actions & modularize screens. ProleInstallerBase (actions.py): Created shared base class with 37 deduplicated methods previously duplicated between ProleSilentInstaller and ProleInstaller. Namespace, environment, secret, deployment, port-forward, authority/repair, image, and logging helpers now defined once. Subclasses override _get_input() to bridge their data-access layers. screens.py -> screens/ package (18 mixin modules): Split 10,234-line monolithic screens.py into focused mixin modules: base, navigation, welcome, dependencies, network, environment, database, cluster, services, security, ollama, supabase, docker, build, packaging, deploy, validate, cfg. __init__.py composes ProleInstaller from all mixins and re-exports has_display(), main() for full backward compatibility. All 37 tests pass with no regressions.
97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
import sys
|
|
from unittest.mock import patch, MagicMock
|
|
import pytest
|
|
from pathlib import Path
|
|
|
|
# Mock tkinter and other GUI/macOS specific imports
|
|
sys.modules['tkinter'] = MagicMock()
|
|
sys.modules['tkinter.ttk'] = MagicMock()
|
|
sys.modules['tkinter.scrolledtext'] = MagicMock()
|
|
sys.modules['tkinter.messagebox'] = MagicMock()
|
|
sys.modules['Foundation'] = MagicMock()
|
|
sys.modules['AppKit'] = MagicMock()
|
|
sys.modules['PIL'] = MagicMock()
|
|
sys.modules['PIL.Image'] = MagicMock()
|
|
sys.modules['PIL.ImageTk'] = MagicMock()
|
|
|
|
import install
|
|
from install import ProleInstaller
|
|
|
|
@pytest.fixture
|
|
def mock_installer(tmp_path):
|
|
test_cfg_src = Path(__file__).parent / "fixtures" / "test-prole.cfg"
|
|
cfg_path = tmp_path / "prole.cfg"
|
|
cfg_path.write_text(test_cfg_src.read_text())
|
|
|
|
with patch('install.tk.Tk'), \
|
|
patch('install.tk.Frame'), \
|
|
patch('install.tk.Canvas'), \
|
|
patch('install.tk.Label'), \
|
|
patch('install.tk.Button'), \
|
|
patch('install.tk.StringVar'), \
|
|
patch('install.tk.BooleanVar'), \
|
|
patch('install.tk.IntVar'), \
|
|
patch('install.ttk.Style'), \
|
|
patch('install.Path.exists', return_value=True):
|
|
|
|
root = MagicMock()
|
|
root.winfo_screenwidth.return_value = 1920
|
|
root.winfo_screenheight.return_value = 1080
|
|
|
|
app = ProleInstaller(root, config_path=str(cfg_path))
|
|
app.bg_canvas.winfo_width.return_value = 1000
|
|
return app
|
|
|
|
def test_service_layer_navigation_pause(mock_installer):
|
|
# Go to init_cluster
|
|
mock_installer.show_page('init_cluster')
|
|
|
|
# Mock cluster ready
|
|
with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True):
|
|
# Click Next - should NOT go to init_password yet, but show overlay
|
|
with patch.object(mock_installer, '_run_service_layer_overlay') as mock_overlay:
|
|
mock_installer.on_next()
|
|
mock_overlay.assert_called_once()
|
|
assert mock_installer.pages[mock_installer.page_index][0] == 'init_cluster'
|
|
|
|
def test_service_layer_overlay_behavior(mock_installer):
|
|
mock_installer.show_page('init_cluster')
|
|
|
|
# Trigger overlay
|
|
with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True), \
|
|
patch.object(mock_installer, '_create_console_output'):
|
|
mock_installer.on_next()
|
|
|
|
assert mock_installer._showing_service_overlay is True
|
|
assert mock_installer._common_services_success is False
|
|
|
|
# Next button should be disabled via update_footer
|
|
mock_installer.next_button = MagicMock()
|
|
mock_installer.prev_button = MagicMock()
|
|
mock_installer.update_footer()
|
|
mock_installer.next_button.configure.assert_any_call(state='disabled')
|
|
|
|
# Simulate success
|
|
mock_installer._common_services_success = True
|
|
mock_installer.next_button.reset_mock()
|
|
mock_installer.update_footer()
|
|
mock_installer.next_button.configure.assert_any_call(state='normal')
|
|
|
|
# Now Next should proceed
|
|
with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True):
|
|
mock_installer.on_next()
|
|
assert mock_installer.pages[mock_installer.page_index][0] == 'init_password'
|
|
|
|
def test_service_layer_prev_behavior(mock_installer):
|
|
mock_installer.show_page('init_cluster')
|
|
with patch.object(mock_installer, '_cluster_ready_for_navigation', return_value=True), \
|
|
patch.object(mock_installer, '_create_console_output'):
|
|
mock_installer.on_next()
|
|
|
|
assert mock_installer._showing_service_overlay is True
|
|
|
|
# Prev from overlay should go back to init_cluster and reset overlay flag
|
|
mock_installer.on_prev()
|
|
assert mock_installer._showing_service_overlay is False
|
|
assert mock_installer.pages[mock_installer.page_index][0] == 'init_cluster'
|