refactor: stage effective view resolution

This commit is contained in:
2026-07-29 19:09:03 +02:00
parent c24d1f2ca4
commit d5b13decfc
4 changed files with 694 additions and 224 deletions
+177
View File
@@ -128,12 +128,14 @@ class ViewsServiceTests(unittest.TestCase):
*,
name: str,
scope_type: str = "tenant",
scope_id: str | None = None,
visible_surface_ids: list[str] | None = None,
):
definition = create_definition(
self.session,
tenant_id="tenant-1",
scope_type=scope_type,
scope_id=scope_id,
definition_key=None,
name=name,
description=None,
@@ -154,6 +156,30 @@ class ViewsServiceTests(unittest.TestCase):
)
return definition
def assign(
self,
definition,
*,
scope_type: str,
scope_id: str | None,
mode: str = "default",
priority: int = 0,
):
return create_assignment(
self.session,
tenant_id="tenant-1",
scope_type=scope_type,
scope_id=scope_id,
definition=definition,
revision_id=None,
mode=mode,
priority=priority,
is_active=True,
metadata={},
catalogue=self.catalogue,
actor_id="account-admin",
)
def test_normalization_adds_required_surfaces_and_ancestors(self) -> None:
normalized = normalize_visible_surface_ids(
ordinary_surface_ids(),
@@ -311,6 +337,157 @@ class ViewsServiceTests(unittest.TestCase):
{option.id for option in state.available_views},
)
def test_scope_precedence_is_system_tenant_group_user(self) -> None:
definitions = {
"system": self.create_published_definition(
name="System",
scope_type="system",
),
"tenant": self.create_published_definition(name="Tenant"),
"group": self.create_published_definition(
name="Group",
scope_type="group",
scope_id="group-1",
),
"user": self.create_published_definition(
name="User",
scope_type="user",
scope_id="account-user",
),
}
self.assign(
definitions["system"],
scope_type="system",
scope_id=None,
priority=1000,
)
self.assign(
definitions["tenant"],
scope_type="tenant",
scope_id=None,
priority=100,
)
self.assign(
definitions["group"],
scope_type="group",
scope_id="group-1",
priority=10,
)
self.assign(
definitions["user"],
scope_type="user",
scope_id="account-user",
priority=0,
)
cases = (
("account-outsider", (), "tenant"),
("account-member", ("group-1",), "group"),
("account-user", ("group-1",), "user"),
)
for account_id, group_ids, expected_scope in cases:
with self.subTest(account_id=account_id):
state = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id=account_id,
group_ids=group_ids,
)
self.assertEqual(
definitions[expected_scope].id,
state.effective.view_id,
)
def test_required_then_workflow_then_user_and_default_precedence(self) -> None:
default = self.create_published_definition(name="Default")
workflow = self.create_published_definition(name="Workflow")
required = self.create_published_definition(
name="Required",
visible_surface_ids=lockout_safe_surface_ids(),
)
self.assign(
default,
scope_type="tenant",
scope_id=None,
mode="default",
)
self.assign(
workflow,
scope_type="tenant",
scope_id=None,
mode="available",
)
workflow_state = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id="account-user",
workflow_view_id=workflow.id,
)
self.assertEqual(workflow.id, workflow_state.effective.view_id)
self.assertEqual(
"workflow_selection",
workflow_state.effective.provenance[0]["source"],
)
self.assign(
required,
scope_type="tenant",
scope_id=None,
mode="required",
)
required_state = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id="account-user",
catalogue=self.catalogue,
workflow_view_id=workflow.id,
)
self.assertEqual(required.id, required_state.effective.view_id)
self.assertTrue(required_state.effective.locked)
self.assertEqual(
"required_assignment",
required_state.effective.provenance[0]["source"],
)
def test_resolution_invalidation_token_tracks_inputs(self) -> None:
definition = self.create_published_definition(name="Files")
self.assign(
definition,
scope_type="tenant",
scope_id=None,
mode="available",
)
baseline = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id="account-user",
catalogue=self.catalogue,
)
workflow = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id="account-user",
catalogue=self.catalogue,
workflow_view_id=definition.id,
)
reduced_catalogue = resolve_effective_view(
self.session,
tenant_id="tenant-1",
account_id="account-user",
catalogue=self.catalogue[:-1],
)
self.assertIsNotNone(baseline.invalidation_token)
self.assertNotEqual(
baseline.invalidation_token,
workflow.invalidation_token,
)
self.assertNotEqual(
baseline.invalidation_token,
reduced_catalogue.invalidation_token,
)
def test_more_specific_available_assignment_cannot_override_required_mode(
self,
) -> None: