from __future__ import annotations import pytest from govoplan_core.core.appearance import ( normalize_appearance_overrides, resolve_effective_appearance, update_appearance_custom_overrides_policy, update_appearance_settings, ) def _overrides() -> dict[str, object]: return { "schema_version": "1", "light": { "accent": "#245f91", "accent_foreground": "#ffffff", "surface": "#ffffff", "surface_foreground": "#303135", "success": "#d8eee8", "success_foreground": "#315f55", "info": "#dce9f3", "info_foreground": "#294a61", "warning": "#ffe1a3", "warning_foreground": "#593700", "danger": "#f8d1cc", "danger_foreground": "#873c35", }, "dark": { "accent": "#7ea6c5", "accent_foreground": "#242424", "surface": "#262724", "surface_foreground": "#f1f1f1", "success": "#24473f", "success_foreground": "#d8eee8", "info": "#243d4e", "info_foreground": "#dce9f3", "warning": "#5a431f", "warning_foreground": "#ffe1a3", "danger": "#4f2d2a", "danger_foreground": "#f8d1cc", }, } def test_appearance_precedence_and_inheritance() -> None: decision = resolve_effective_appearance( system_settings={"appearance": {"default_palette": "civic_blue"}}, tenant_settings={"appearance": {"default_palette": "forest"}}, user_settings={"ui": {"palette": "plum"}}, ) assert (decision.palette, decision.source, decision.locked) == ("plum", "user", False) inherited = resolve_effective_appearance( system_settings={"appearance": {"default_palette": "civic_blue"}}, tenant_settings={"appearance": {"default_palette": "forest"}}, user_settings={"ui": {"palette": None}}, ) assert (inherited.palette, inherited.source) == ("forest", "tenant") def test_system_lock_wins_and_invalid_values_fail_safe() -> None: decision = resolve_effective_appearance( system_settings={"appearance": {"default_palette": "civic_blue", "palette_locked": True}}, tenant_settings={"appearance": {"default_palette": "forest", "palette_locked": True}}, user_settings={"ui": {"palette": "plum"}}, ) assert decision.as_dict() == { "palette": "civic_blue", "source": "system_lock", "locked": True, "system_default_palette": "civic_blue", "tenant_default_palette": "forest", "inherited_palette": "civic_blue", "custom_overrides": None, "custom_overrides_allowed": False, } fallback = resolve_effective_appearance( system_settings={"appearance": {"default_palette": "unsafe"}}, tenant_settings={}, user_settings={"ui": {"palette": "unknown"}}, ) assert (fallback.palette, fallback.source) == ("default", "system") def test_appearance_settings_reset_without_touching_neighbors() -> None: configured = update_appearance_settings( {"neighbor": {"kept": True}}, default_palette="forest", palette_locked=True, ) assert configured["neighbor"] == {"kept": True} assert configured["appearance"] == {"default_palette": "forest", "palette_locked": True} assert update_appearance_settings(configured, default_palette=None, palette_locked=False) == { "neighbor": {"kept": True} } def test_custom_overrides_require_system_and_tenant_policy_and_validate_both_modes() -> None: document = _overrides() normalized = normalize_appearance_overrides(document) assert normalized == document decision = resolve_effective_appearance( system_settings={"appearance": {"allow_custom_overrides": True}}, tenant_settings={"appearance": {"allow_custom_overrides": True}}, user_settings={"ui": {"appearance_overrides": document}}, ) assert decision.custom_overrides_allowed is True assert decision.custom_overrides == document blocked = resolve_effective_appearance( system_settings={"appearance": {"allow_custom_overrides": True}}, tenant_settings={"appearance": {"allow_custom_overrides": False}}, user_settings={"ui": {"appearance_overrides": document}}, ) assert blocked.custom_overrides_allowed is False assert blocked.custom_overrides is None invalid = _overrides() invalid["dark"]["danger"] = invalid["dark"]["warning"] # type: ignore[index] with pytest.raises(ValueError, match="visibly distinct"): normalize_appearance_overrides(invalid) low_contrast = _overrides() low_contrast["light"]["accent_foreground"] = "#245f91" # type: ignore[index] with pytest.raises(ValueError, match="WCAG AA"): normalize_appearance_overrides(low_contrast) def test_custom_override_policy_update_preserves_neighboring_appearance_settings() -> None: configured = update_appearance_custom_overrides_policy( {"appearance": {"default_palette": "forest"}}, allowed=True, ) assert configured == {"appearance": {"default_palette": "forest", "allow_custom_overrides": True}} assert update_appearance_custom_overrides_policy(configured, allowed=None) == { "appearance": {"default_palette": "forest"} }