Files
kernalix7--winpodx/tests/test_reverse_open_panel.py
wehub-resource-sync 3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

188 lines
5.4 KiB
Python

# SPDX-License-Identifier: MIT
"""Tests for the pure-Python helpers in ``winpodx.gui.reverse_open_panel``.
The Qt widget builder (:func:`build_panel`) requires PySide6 and an
X / Wayland display, so it's not exercised here — the existing GUI
test gap covers that. The slug + list helpers + status snapshot are
all Qt-free and important enough to pin.
"""
from __future__ import annotations
from winpodx.core.config import Config
from winpodx.gui.reverse_open_panel import (
PanelStatus,
add_slug,
build_panel_status,
format_status_line,
remove_slug,
validate_slug,
)
# --- validate_slug -----------------------------------------------------------
def test_validate_slug_accepts_kebab() -> None:
ok, value = validate_slug("org-kde-kate")
assert ok is True
assert value == "org-kde-kate"
def test_validate_slug_lowercases_input() -> None:
ok, value = validate_slug("Org.KDE.Kate")
# The grammar excludes '.', so this returns invalid AFTER lowering.
assert ok is False
assert "must match" in value
def test_validate_slug_strips_whitespace() -> None:
ok, value = validate_slug(" kate ")
assert ok is True
assert value == "kate"
def test_validate_slug_rejects_empty() -> None:
ok, msg = validate_slug("")
assert ok is False
assert "empty" in msg
def test_validate_slug_rejects_spaces() -> None:
ok, msg = validate_slug("two words")
assert ok is False
assert "must match" in msg
def test_validate_slug_rejects_uppercase() -> None:
# Note: the helper lowercases first then validates, so this only
# fails because of an internal character — pure uppercase passes
# via the lowercase step.
ok, _ = validate_slug("Kate")
assert ok is True
# --- add_slug / remove_slug --------------------------------------------------
def test_add_slug_to_empty() -> None:
changed, cur, other, msg = add_slug([], ["evil"], "kate")
assert changed is True
assert cur == ["kate"]
assert other == ["evil"]
assert "added" in msg
def test_add_slug_idempotent() -> None:
changed, cur, other, msg = add_slug(["kate"], [], "kate")
assert changed is False
assert cur == ["kate"]
assert "already present" in msg
def test_add_slug_strips_from_other_list() -> None:
changed, cur, other, _ = add_slug(["existing"], ["kate"], "kate")
assert changed is True
assert cur == ["existing", "kate"]
assert other == []
def test_add_slug_keeps_results_sorted() -> None:
_, cur, _, _ = add_slug(["b", "d"], [], "a")
assert cur == ["a", "b", "d"]
def test_remove_slug_present() -> None:
changed, cur, msg = remove_slug(["a", "b", "c"], "b")
assert changed is True
assert cur == ["a", "c"]
assert "removed" in msg
def test_remove_slug_missing() -> None:
changed, cur, msg = remove_slug(["a", "b"], "z")
assert changed is False
assert cur == ["a", "b"]
assert "not present" in msg
# --- build_panel_status ------------------------------------------------------
def _cfg_with(enabled: bool, allow: list[str], deny: list[str]) -> Config:
cfg = Config()
cfg.reverse_open.enabled = enabled
cfg.reverse_open.allowlist = list(allow)
cfg.reverse_open.denylist = list(deny)
# Re-run __post_init__ so the DANGEROUS_DEFAULTS fold doesn't
# surprise the test caller — keep the lists exactly as set.
return cfg
def test_build_panel_status_no_manifest() -> None:
cfg = _cfg_with(False, [], [])
status = build_panel_status(cfg, None)
assert status.enabled is False
assert status.daemon_running is False
assert status.daemon_pid is None
assert status.cached_app_count is None
assert status.cached_generated_at is None
assert status.allowlist == []
def test_build_panel_status_with_manifest() -> None:
cfg = _cfg_with(True, ["kate"], ["code"])
manifest = {
"version": 1,
"generated_at": "2026-05-11T12:00:00Z",
"apps": [{"slug": "kate"}, {"slug": "gimp"}, {"slug": "vlc"}],
}
status = build_panel_status(cfg, manifest)
assert status.enabled is True
assert status.cached_app_count == 3
assert status.cached_generated_at == "2026-05-11T12:00:00Z"
assert status.allowlist == ["kate"]
def test_build_panel_status_tolerates_malformed_manifest() -> None:
cfg = _cfg_with(False, [], [])
# `apps` not a list, `generated_at` not a string → both fields fall
# back to None without raising.
status = build_panel_status(cfg, {"apps": "not a list", "generated_at": 42})
assert status.cached_app_count is None
assert status.cached_generated_at is None
# --- format_status_line ------------------------------------------------------
def test_format_status_line_disabled_no_daemon() -> None:
status = PanelStatus(
enabled=False,
daemon_running=False,
daemon_pid=None,
cached_app_count=None,
cached_generated_at=None,
allowlist=[],
denylist=[],
)
text = format_status_line(status)
assert "disabled" in text
assert "Daemon stopped" in text
assert "no manifest" in text
def test_format_status_line_enabled_running() -> None:
status = PanelStatus(
enabled=True,
daemon_running=True,
daemon_pid=4321,
cached_app_count=12,
cached_generated_at="2026-05-11T12:00:00Z",
allowlist=[],
denylist=[],
)
text = format_status_line(status)
assert "enabled" in text
assert "pid 4321" in text
assert "12 apps cached" in text