prole/tests/test_navigation.py
chrisfu edbc6c5385 Checkpoint: Refactor installer UI and update Supabase deployment strategy
- Refactored install.py and installer package for improved UI and navigation.

- Replaced Supabase k8s manifests with a dedicated deployment script and port-wiring logic.

- Added new deployment pipeline and finalization scripts in etc/.

- Updated initialization scripts for Kerberos, authority, and port forwards.

- Updated port mappings and tests.
2026-02-03 14:55:05 -08:00

142 lines
5.6 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():
with patch('install.tk.Tk'), \
patch('install.tk.Frame'), \
patch('install.tk.Canvas'), \
patch('install.tk.Label'), \
patch('install.tk.Button'), \
patch('install.ttk.Style'), \
patch('install.Path.exists', return_value=True):
root = MagicMock()
# Mock winfo methods needed for centering
root.winfo_screenwidth.return_value = 1920
root.winfo_screenheight.return_value = 1080
app = ProleInstaller(root)
return app
def test_initial_page(mock_installer):
# Should start at index 0 (welcome)
assert mock_installer.page_index == 0
assert mock_installer.pages[mock_installer.page_index][0] == 'welcome'
def test_show_page_by_id(mock_installer):
mock_installer.show_page('network_scan')
assert mock_installer.pages[mock_installer.page_index][0] == 'network_scan'
def test_show_page_invalid_id(mock_installer):
# Current page is welcome (0)
mock_installer.page_index = 0
with patch('install.messagebox.showerror') as mock_error:
mock_installer.show_page('non_existent_page')
# Should stay at current index and NOT fallback to 0 if it was elsewhere,
# or just stay at 0 if it was at 0.
# The key fix was NOT defaulting to 0 when index_or_id is a string and not found.
assert mock_installer.page_index == 0
mock_error.assert_called_once()
def test_navigation_flow_standard(mock_installer):
# Welcome -> Dependencies
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'deps_summary'
# Mock all dependencies installed to skip dep pages
with patch.object(mock_installer, 'all_dependencies_installed', return_value=True):
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'network_scan'
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'env_setup'
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'init_cluster'
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_navigation_flow_missing_deps(mock_installer):
# Welcome -> Dependencies
mock_installer.show_page('welcome')
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'deps_summary'
# Mock one dependency missing
mock_dep_id = mock_installer.dependencies[0]['id']
with patch.object(mock_installer, 'all_dependencies_installed', return_value=False), \
patch.object(mock_installer, '_dep_navigation_sequence', return_value=[f'dep_{mock_dep_id}']):
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == f'dep_{mock_dep_id}'
# Next from dep page with all installed now
with patch.object(mock_installer, 'all_dependencies_installed', return_value=True):
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'network_scan'
def test_on_prev(mock_installer):
# network_scan -> deps_summary
mock_installer.show_page('network_scan')
with patch.object(mock_installer, 'all_dependencies_installed', return_value=True):
mock_installer.on_prev()
assert mock_installer.pages[mock_installer.page_index][0] == 'deps_summary'
# deps_summary -> welcome
mock_installer.on_prev()
assert mock_installer.pages[mock_installer.page_index][0] == 'welcome'
def test_infinite_loop_prevention(mock_installer):
# This specifically tests the scenario that caused the bug:
# trying to navigate to a missing ID should NOT reset to welcome (0)
mock_installer.show_page('network_scan')
current_idx = mock_installer.page_index
mock_installer.show_page('invalid_id')
assert mock_installer.page_index == current_idx
assert mock_installer.pages[mock_installer.page_index][0] == 'network_scan'
def test_on_next_finish_loop(mock_installer):
# From last page, on_next should not crash or loop
last_idx = len(mock_installer.pages) - 1
mock_installer.show_page(last_idx)
mock_installer.on_next()
assert mock_installer.page_index == last_idx
def test_on_prev_first_page(mock_installer):
# From first page, on_prev should not crash or loop
mock_installer.show_page(0)
mock_installer.on_prev()
assert mock_installer.page_index == 0
def test_navigation_to_create_installer(mock_installer):
# From build page, if successful, next should go to create_installer
mock_installer.show_page('build')
mock_installer._built_success = True
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == 'create_installer'
# Last page Finish label
mock_installer.update_footer()
mock_installer.next_button.configure.assert_any_call(text='Finish')