prole/tests/installer/test_cluster_save_triggers_status_check.py
chrisfu 4ee2b259c9 Checkpoint: rename installer to knoe + harden db build context
- Add build-context helper to copy Docker context safely (ignore runtime data, keep symlinks)

- Update UI and core actions to use ~/.prole/build and shared copy helper

- Add/adjust tests and scripts; introduce knoe ops helpers and update manifests

Co-authored-by: Junie <junie@jetbrains.com>
2026-03-22 01:45:21 -07:00

131 lines
4.6 KiB
Python

import sys
import tkinter as tk
from pathlib import Path
from unittest.mock import MagicMock
import pytest
# Ensure `installer` is importable when tests are invoked directly via `pytest`
PROJECT_ROOT = Path(__file__).resolve().parents[2]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
import knoe.ui.screens.cluster as cluster_mod
from knoe.ui.screens.base import ScreenBaseMixin
from knoe.ui.screens.cluster import ClusterScreenMixin
class _DummyClusterSaveApp(ScreenBaseMixin, ClusterScreenMixin):
"""Minimal surface to exercise the Cluster Environment screen Save behavior."""
def __init__(self, root: tk.Tk):
self.root = root
# Canvas is not required by `_validate_and_save_cluster_config`, but other mixins
# assume it exists.
self.bg_canvas = tk.Canvas(root, width=1, height=1)
self._canvas_items = []
self._overlay_widgets = []
self.cluster_env = tk.StringVar(master=root, value="service")
self.selected_k3d_cluster = tk.StringVar(master=root, value="")
self.selected_kubectx = tk.StringVar(master=root, value="")
self.service_namespace = tk.StringVar(master=root, value="knoe-system")
self.prod_artifacts_path = tk.StringVar(master=root, value=str(Path.home()))
self.k3s_server_url = tk.StringVar(master=root, value="")
self.k3s_token = tk.StringVar(master=root, value="")
self.supabase_enabled = tk.BooleanVar(master=root, value=False)
self.gitops_enabled = tk.BooleanVar(master=root, value=False)
self.kerberos_enabled = tk.BooleanVar(master=root, value=False)
self.at_rest_encryption_enabled = tk.BooleanVar(master=root, value=False)
# Minimal config structure required by `_validate_and_save_cluster_config`.
self.prole_cfg_data = {
"Initialize Cluster": {
"ENVIRONMENT": "service",
"K3S_SERVER_URL": "",
"K3S_TOKEN": "",
},
"Global": {
# Keep old/new equal so we don't trigger the namespace cleanup prompt.
"SERVICE_NAMESPACE": "knoe-system",
},
"Optional Features": {},
"Prod Cluster (k8s)": {},
}
self.saved_calls = 0
self.status_check_calls = 0
def _save_prole_cfg(self):
self.saved_calls += 1
def _verify_k3s_services(self):
self.status_check_calls += 1
def test_cluster_env_save_triggers_status_check(monkeypatch):
# Some test modules replace tkinter with MagicMocks at import-time.
if isinstance(sys.modules.get("tkinter"), MagicMock):
pytest.skip("tkinter is mocked in this test run")
# Avoid depending on a real kubeconfig file.
monkeypatch.setattr(cluster_mod, "_find_kubeconfig_file", lambda: "/tmp/kubeconfig")
# Avoid any real secret handling.
monkeypatch.setattr(cluster_mod, "_encrypt_cfg_secret", lambda s: s)
root = tk.Tk()
root.withdraw()
try:
app = _DummyClusterSaveApp(root)
assert app._validate_and_save_cluster_config() is True
assert app.saved_calls == 1
# Requirement: clicking Save should immediately run a status check.
assert app.status_check_calls == 1
finally:
try:
root.destroy()
except Exception:
pass
def test_cluster_env_prod_artifacts_dir_expands_prole_home_from_cfg(monkeypatch, tmp_path):
# Some test modules replace tkinter with MagicMocks at import-time.
if isinstance(sys.modules.get("tkinter"), MagicMock):
pytest.skip("tkinter is mocked in this test run")
# Ensure `$PROLE_HOME` is NOT available as an OS env var; it must come from config.
monkeypatch.delenv("PROLE_HOME", raising=False)
monkeypatch.chdir(tmp_path)
root = tk.Tk()
root.withdraw()
try:
app = _DummyClusterSaveApp(root)
# Switch to prod so the artifacts directory is validated/created.
app.cluster_env.set("prod")
# Provide PROLE_HOME via config data only.
prole_home = tmp_path / "prole-home"
prole_home.mkdir(parents=True, exist_ok=True)
app.prole_cfg_data.setdefault("System Environment", {})["PROLE_HOME"] = str(
prole_home
)
# Use a path expression that previously would create a literal `$PROLE_HOME` dir.
app.prod_artifacts_path.set("$PROLE_HOME/staging")
assert app._validate_and_save_cluster_config() is True
assert (prole_home / "staging").is_dir()
assert not (tmp_path / "$PROLE_HOME" / "staging").exists()
finally:
try:
root.destroy()
except Exception:
pass