- _on_realize implementiert (war nur connected, nicht definiert) - do_unrealize für as_file() Context-Manager-Cleanup - threading.Lock für _greetd_sock Race Condition - TOML-Parsing-Fehler abfangen statt Crash - last-user Datei: Längen- und Zeichenvalidierung - detect_locale: non-alpha LANG-Werte abweisen - exec_cmd Plausibility-Check mit shutil.which - Exception-Details ins Log statt in die UI - subprocess.run Timeout für Power-Actions - Sequence[Path] statt tuple[Path, ...] in get_sessions - Mock-Server _recvall für fragmentierte Reads - [behavior]-Config-Sektion entfernt (unimplementiert) - Design Decisions in CLAUDE.md dokumentiert
127 lines
3.9 KiB
Python
127 lines
3.9 KiB
Python
# ABOUTME: Tests for locale detection and string lookup.
|
|
# ABOUTME: Verifies DE/EN selection based on system locale.
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from moongreet.i18n import detect_locale, load_strings, Strings
|
|
|
|
|
|
class TestDetectLocale:
|
|
"""Tests for system locale detection."""
|
|
|
|
def test_reads_lang_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "de_DE.UTF-8")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "de"
|
|
|
|
def test_reads_lang_without_region(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "en_US.UTF-8")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "en"
|
|
|
|
def test_falls_back_to_locale_conf(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.delenv("LANG", raising=False)
|
|
locale_conf = tmp_path / "locale.conf"
|
|
locale_conf.write_text("LANG=de_AT.UTF-8\n")
|
|
|
|
result = detect_locale(locale_conf_path=locale_conf)
|
|
|
|
assert result == "de"
|
|
|
|
def test_defaults_to_english(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.delenv("LANG", raising=False)
|
|
missing = tmp_path / "nonexistent"
|
|
|
|
result = detect_locale(locale_conf_path=missing)
|
|
|
|
assert result == "en"
|
|
|
|
def test_handles_bare_language_code(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "de")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "de"
|
|
|
|
def test_handles_c_locale(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "C")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "en"
|
|
|
|
def test_handles_posix_locale(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "POSIX")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "en"
|
|
|
|
def test_rejects_non_alpha_lang(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("LANG", "../../etc")
|
|
|
|
result = detect_locale()
|
|
|
|
assert result == "en"
|
|
|
|
|
|
class TestLoadStrings:
|
|
"""Tests for loading the correct string table."""
|
|
|
|
def test_loads_german_strings(self) -> None:
|
|
strings = load_strings("de")
|
|
|
|
assert strings.password_placeholder == "Passwort"
|
|
assert strings.reboot_tooltip == "Neustart"
|
|
assert strings.shutdown_tooltip == "Herunterfahren"
|
|
|
|
def test_loads_english_strings(self) -> None:
|
|
strings = load_strings("en")
|
|
|
|
assert strings.password_placeholder == "Password"
|
|
assert strings.reboot_tooltip == "Reboot"
|
|
assert strings.shutdown_tooltip == "Shut down"
|
|
|
|
def test_unknown_locale_falls_back_to_english(self) -> None:
|
|
strings = load_strings("fr")
|
|
|
|
assert strings.password_placeholder == "Password"
|
|
|
|
def test_returns_strings_dataclass(self) -> None:
|
|
strings = load_strings("de")
|
|
|
|
assert isinstance(strings, Strings)
|
|
|
|
def test_error_messages_are_present(self) -> None:
|
|
strings = load_strings("en")
|
|
|
|
assert strings.wrong_password
|
|
assert strings.auth_failed
|
|
assert strings.reboot_failed
|
|
assert strings.shutdown_failed
|
|
assert strings.no_session_selected
|
|
assert strings.multi_stage_unsupported
|
|
assert strings.invalid_session_command
|
|
assert strings.session_start_failed
|
|
assert strings.faillock_locked
|
|
|
|
def test_faillock_warning_template(self) -> None:
|
|
strings = load_strings("de")
|
|
|
|
# Template should accept an int for remaining attempts
|
|
result = strings.faillock_attempts_remaining.format(n=1)
|
|
assert "1" in result
|
|
|
|
def test_connection_error_is_generic(self) -> None:
|
|
strings = load_strings("en")
|
|
|
|
# Error messages should not contain format placeholders (no info leakage)
|
|
assert "{" not in strings.connection_error
|
|
assert "{" not in strings.socket_error
|