111 lines
5.8 KiB
Python
111 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from govoplan_core.core.navigation import (
|
|
NavigationPreferences,
|
|
NavigationSeparator,
|
|
navigation_preferences_from_settings,
|
|
resolve_navigation_preferences,
|
|
update_navigation_preferences,
|
|
)
|
|
|
|
|
|
class NavigationPreferenceTests(unittest.TestCase):
|
|
def test_separator_order_and_labels_round_trip_across_scopes(self) -> None:
|
|
separator = NavigationSeparator("separator:work", "Arbeit")
|
|
preferences = NavigationPreferences(order=("dashboard", separator.id, "files", "mail"), separators=(separator,))
|
|
stored = navigation_preferences_from_settings(update_navigation_preferences({}, preferences))
|
|
self.assertEqual(preferences, stored)
|
|
resolved = resolve_navigation_preferences(("dashboard", "files", "mail"), system=stored)
|
|
self.assertIsNone(resolved["dashboard"].section)
|
|
self.assertEqual(separator, resolved["files"].section)
|
|
self.assertEqual(separator, resolved["mail"].section)
|
|
self.assertTrue(resolved["dashboard"].custom_layout)
|
|
self.assertEqual("system", resolved["files"].layout_source)
|
|
self.assertNotIn(separator.id, resolved) # Never an authorized destination.
|
|
|
|
def test_personal_separator_override_and_flat_reset_preserve_locks(self) -> None:
|
|
system = NavigationPreferences(order=("separator:system", "files", "mail"), separators=(NavigationSeparator("separator:system", "System"),), locked=("files",))
|
|
user = NavigationPreferences(order=("mail", "separator:user", "files"), hidden=("files",), separators=(NavigationSeparator("separator:user", "Persönlich"),))
|
|
resolved = resolve_navigation_preferences(("files", "mail"), system=system, user=user)
|
|
self.assertEqual("user", resolved["files"].layout_source)
|
|
self.assertEqual("Persönlich", resolved["files"].section.label)
|
|
self.assertTrue(resolved["files"].visible)
|
|
self.assertIsNone(resolved["mail"].section)
|
|
flat = resolve_navigation_preferences(("files", "mail"), system=system, user=NavigationPreferences(separators=()))
|
|
self.assertTrue(flat["files"].custom_layout)
|
|
self.assertIsNone(flat["files"].section)
|
|
self.assertTrue(flat["files"].locked)
|
|
|
|
def test_legacy_preferences_inherit_separator_layout(self) -> None:
|
|
separator = NavigationSeparator("separator:work", "Work")
|
|
resolved = resolve_navigation_preferences(("files", "mail"), system=NavigationPreferences(order=(separator.id, "files", "mail"), separators=(separator,)), user=NavigationPreferences(hidden=("mail",)))
|
|
self.assertEqual(separator, resolved["files"].section)
|
|
self.assertEqual("system", resolved["files"].layout_source)
|
|
self.assertFalse(resolved["mail"].visible)
|
|
|
|
def test_separator_schema_rejects_unsafe_and_unknown_fields(self) -> None:
|
|
from pydantic import ValidationError
|
|
from govoplan_core.api.v1.schemas import NavigationPreferencesPayload
|
|
for separator in ({"id": "files", "label": "Invalid"}, {"id": "separator:ok", "label": "bad\nlabel"}, {"id": "separator:ok", "route": "/admin"}):
|
|
with self.subTest(separator=separator), self.assertRaises(ValidationError):
|
|
NavigationPreferencesPayload.model_validate({"separators": [separator]})
|
|
|
|
def test_user_order_overrides_tenant_and_system_order(self) -> None:
|
|
resolved = resolve_navigation_preferences(
|
|
("dashboard", "files", "mail", "campaign"),
|
|
system=NavigationPreferences(order=("mail", "files")),
|
|
tenant=NavigationPreferences(order=("campaign", "mail")),
|
|
user=NavigationPreferences(order=("files", "campaign")),
|
|
)
|
|
|
|
self.assertEqual(
|
|
["files", "campaign", "mail", "dashboard"],
|
|
[item.id for item in sorted(resolved.values(), key=lambda item: item.order)],
|
|
)
|
|
self.assertEqual("user", resolved["files"].order_source)
|
|
self.assertEqual("user", resolved["campaign"].order_source)
|
|
|
|
def test_lower_scope_cannot_hide_locked_item(self) -> None:
|
|
resolved = resolve_navigation_preferences(
|
|
("dashboard", "files", "mail"),
|
|
system=NavigationPreferences(locked=("dashboard",)),
|
|
tenant=NavigationPreferences(hidden=("dashboard", "files"), locked=("mail",)),
|
|
user=NavigationPreferences(hidden=("dashboard", "mail")),
|
|
)
|
|
|
|
self.assertTrue(resolved["dashboard"].visible)
|
|
self.assertTrue(resolved["dashboard"].locked)
|
|
self.assertEqual("system", resolved["dashboard"].lock_source)
|
|
self.assertTrue(resolved["mail"].visible)
|
|
self.assertEqual("tenant", resolved["mail"].lock_source)
|
|
self.assertTrue(resolved["files"].visible)
|
|
self.assertEqual("user", resolved["files"].visibility_source)
|
|
|
|
def test_higher_scope_visibility_replaces_inherited_preference(self) -> None:
|
|
resolved = resolve_navigation_preferences(
|
|
("files", "mail"),
|
|
system=NavigationPreferences(hidden=("mail",)),
|
|
tenant=NavigationPreferences(hidden=("files",)),
|
|
)
|
|
|
|
self.assertTrue(resolved["mail"].visible)
|
|
self.assertFalse(resolved["files"].visible)
|
|
self.assertEqual("tenant", resolved["mail"].visibility_source)
|
|
|
|
def test_settings_round_trip_is_bounded_and_normalized(self) -> None:
|
|
settings = update_navigation_preferences(
|
|
{"unrelated": {"preserved": True}},
|
|
NavigationPreferences(order=(" files ", "files", "mail"), hidden=("mail",)),
|
|
)
|
|
parsed = navigation_preferences_from_settings(settings)
|
|
|
|
self.assertEqual(("files", "mail"), parsed.order if parsed else ())
|
|
self.assertEqual(("mail",), parsed.hidden if parsed else ())
|
|
self.assertEqual({"preserved": True}, settings["unrelated"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|