prole/tests/test_service_layer_navigation.py

115 lines
4.5 KiB
Python

import sys
from contextlib import ExitStack
from pathlib import Path
from unittest.mock import patch, MagicMock
import pytest
# 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()
from install import KnoeInstaller
@pytest.fixture
def mock_installer(tmp_path):
test_cfg_src = Path(__file__).parent / "fixtures" / "test-knoe.cfg"
cfg_path = tmp_path / "knoe.cfg"
cfg_path.write_text(test_cfg_src.read_text())
with ExitStack() as stack:
stack.enter_context(patch("install.tk.Tk"))
stack.enter_context(patch("install.tk.Frame"))
stack.enter_context(patch("install.tk.Canvas"))
stack.enter_context(patch("install.tk.Label"))
stack.enter_context(patch("install.tk.Button"))
mock_entry = stack.enter_context(patch("install.tk.Entry"))
stack.enter_context(patch("install.tk.StringVar"))
stack.enter_context(patch("install.tk.BooleanVar"))
stack.enter_context(patch("install.tk.IntVar"))
stack.enter_context(patch("install.ttk.Style"))
stack.enter_context(patch("install.Path.exists", return_value=True))
stack.enter_context(patch("install.Path.mkdir"))
stack.enter_context(patch.object(KnoeInstaller, "_save_env_to_file"))
stack.enter_context(patch.object(KnoeInstaller, "_save_knoe_cfg"))
stack.enter_context(patch.object(KnoeInstaller, "_save_ansible_knoe_vault"))
stack.enter_context(patch.object(KnoeInstaller, "_after_env_saved"))
stack.enter_context(patch("threading.Thread"))
mock_entry.return_value.get.return_value.strip.return_value = "mock_val"
root = MagicMock()
root.winfo_screenwidth.return_value = 1920
root.winfo_screenheight.return_value = 1080
app = KnoeInstaller(root, config_path=str(cfg_path))
try:
app.bg_canvas.winfo_width.return_value = 1000
except Exception:
try:
app.bg_canvas.winfo_width = MagicMock(return_value=1000)
except Exception:
pass
# Ensure deterministic gating for the Cluster Nodes screen (multi-node, non-k3d).
try:
app.cluster_env.get.return_value = "knoe-service-cluster"
except Exception:
pass
app.ansible_topology = {
"domain": "knoe.org",
"hosts": {"merlin": "10.0.0.10", "myrddin": "10.0.0.11"},
"groups": {"k3s_hosts": ["merlin", "myrddin"]},
}
return app
def test_init_cluster_next_goes_to_cluster_nodes(mock_installer):
mock_installer.show_page("init_cluster")
with (
patch.object(mock_installer, "_should_show_cluster_nodes_screen", return_value=True),
patch.object(mock_installer, "_cluster_ready_for_navigation", return_value=True),
patch.object(mock_installer, "_validate_and_save_cluster_config", return_value=True),
patch.object(mock_installer, "_run_service_layer_overlay") as mock_overlay,
):
mock_installer.on_next()
# Service-layer overlay gating has been removed; placement happens in Cluster Nodes.
mock_overlay.assert_not_called()
assert mock_installer.pages[mock_installer.page_index][0] == "cluster_nodes"
def test_cluster_nodes_next_proceeds_to_common_services(mock_installer):
mock_installer.show_page("cluster_nodes")
mock_installer._cluster_nodes_current_policy = {
"hosts": {},
"services": {},
}
with patch.object(
mock_installer,
"_validate_and_save_cluster_nodes_policy",
return_value=True,
):
mock_installer.on_next()
assert mock_installer.pages[mock_installer.page_index][0] == "common_services"
def test_cluster_nodes_prev_goes_back_to_cluster_environment(mock_installer):
mock_installer.show_page("cluster_nodes")
mock_installer.on_prev()
assert mock_installer.pages[mock_installer.page_index][0] == "init_cluster"
def test_common_services_prev_goes_back_to_cluster_nodes(mock_installer):
mock_installer.show_page("common_services")
with patch.object(mock_installer, "_should_show_cluster_nodes_screen", return_value=True):
mock_installer.on_prev()
assert mock_installer.pages[mock_installer.page_index][0] == "cluster_nodes"