mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
- Reuse namespace from existing prole.cfg unless env overrides - Support k3s_hosts children groups and prefer k3s_servers as default - Add headless pytest tkinter stubs and blocked-cluster reconciliation tests - Update registry mirror endpoint, port mappings, and prole-db manifests
150 lines
4.1 KiB
Python
150 lines
4.1 KiB
Python
"""Global pytest configuration.
|
|
|
|
CI/autobuild must run unattended and non-interactive.
|
|
|
|
We do **not** globally replace the whole `tkinter` module because some tests
|
|
exercise real widget layout, but we *do* stub popup-oriented modules
|
|
(`tkinter.messagebox`, `tkinter.filedialog`) so no test can block on a dialog.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
try:
|
|
import tkinter as tk
|
|
except Exception: # pragma: no cover
|
|
tk = None # type: ignore[assignment]
|
|
|
|
|
|
def _configure_messagebox(msg: MagicMock) -> None:
|
|
# Confirmation-style prompts: auto-accept.
|
|
for fn in (
|
|
"askyesno",
|
|
"askokcancel",
|
|
"askretrycancel",
|
|
"askyesnocancel",
|
|
):
|
|
getattr(msg, fn).return_value = True
|
|
|
|
# Informational prompts: no-op.
|
|
for fn in (
|
|
"showinfo",
|
|
"showwarning",
|
|
"showerror",
|
|
):
|
|
getattr(msg, fn).return_value = None
|
|
def _install_popup_stubs() -> dict[str, MagicMock]:
|
|
messagebox = MagicMock(name="tkinter.messagebox")
|
|
filedialog = MagicMock(name="tkinter.filedialog")
|
|
_configure_messagebox(messagebox)
|
|
|
|
# Seed sys.modules so `from tkinter import messagebox, filedialog` picks up
|
|
# these stubs without requiring a real UI.
|
|
sys.modules["tkinter.messagebox"] = messagebox
|
|
sys.modules["tkinter.filedialog"] = filedialog
|
|
|
|
# If tkinter is already imported, ensure it points at our stub submodules.
|
|
tk_mod = sys.modules.get("tkinter")
|
|
if tk_mod is not None:
|
|
try:
|
|
setattr(tk_mod, "messagebox", messagebox)
|
|
setattr(tk_mod, "filedialog", filedialog)
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"tkinter.messagebox": messagebox,
|
|
"tkinter.filedialog": filedialog,
|
|
}
|
|
|
|
|
|
_POPUP_STUBS = _install_popup_stubs()
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def _tk_default_root_session():
|
|
"""Ensure a default Tkinter root exists for tests that create variables.
|
|
|
|
Some UI logic uses `tkinter.StringVar`/`BooleanVar` without explicitly
|
|
providing a master, which requires a default root. In headless contexts we
|
|
prefer a `Tcl()` interpreter fallback.
|
|
"""
|
|
|
|
if tk is None:
|
|
yield
|
|
return
|
|
|
|
existing = getattr(tk, "_default_root", None)
|
|
if existing is not None:
|
|
yield
|
|
return
|
|
|
|
root = None
|
|
try:
|
|
root = tk.Tk()
|
|
try:
|
|
root.withdraw()
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
try:
|
|
root = tk.Tcl()
|
|
try:
|
|
tk._default_root = root # type: ignore[attr-defined]
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
root = None
|
|
|
|
try:
|
|
yield
|
|
finally:
|
|
try:
|
|
if root is not None and hasattr(root, "destroy"):
|
|
root.destroy()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
tk._default_root = None # type: ignore[attr-defined]
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _stub_module(name: str, *, is_package: bool = False) -> types.ModuleType:
|
|
m = types.ModuleType(name)
|
|
if is_package:
|
|
# Mark as a package so submodule imports can succeed.
|
|
m.__path__ = [] # type: ignore[attr-defined]
|
|
return m
|
|
|
|
|
|
def _ensure_importable_or_stub(name: str, *, is_package: bool = False) -> None:
|
|
if name in sys.modules:
|
|
return
|
|
try:
|
|
__import__(name)
|
|
except Exception:
|
|
sys.modules[name] = _stub_module(name, is_package=is_package)
|
|
|
|
|
|
# Optional UI/OS modules: only stub when unavailable (keeps real deps when present).
|
|
_ensure_importable_or_stub("Foundation")
|
|
_ensure_importable_or_stub("AppKit")
|
|
_ensure_importable_or_stub("PIL", is_package=True)
|
|
_ensure_importable_or_stub("PIL.Image")
|
|
_ensure_importable_or_stub("PIL.ImageTk")
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_tkinter_stubs_between_tests():
|
|
# Avoid test-order coupling by resetting call history between tests.
|
|
for mod in _POPUP_STUBS.values():
|
|
mod.reset_mock()
|
|
_configure_messagebox(_POPUP_STUBS["tkinter.messagebox"])
|
|
yield
|