76 lines
2.7 KiB
Python
76 lines
2.7 KiB
Python
"""Control-plane domain invariants."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
from ragcore.domain.control import (
|
|
CrossWorkspaceRelationship,
|
|
InvalidEntity,
|
|
Role,
|
|
Scope,
|
|
Workspace,
|
|
)
|
|
|
|
|
|
def test_workspace_creates_typed_control_plane_entities() -> None:
|
|
workspace = Workspace(uuid4(), "Platform", "platform")
|
|
application = workspace.create_application(
|
|
id=uuid4(), name="Search client", scopes={Scope.SEARCH, Scope.DOCUMENTS_READ}
|
|
)
|
|
space = workspace.create_knowledge_space(
|
|
id=uuid4(), name="Runbooks", slug="runbooks", retention_days=30
|
|
)
|
|
service_account = application.create_service_account(
|
|
id=uuid4(), name="worker", scopes={Scope.SEARCH}
|
|
)
|
|
policy = workspace.create_policy(
|
|
id=uuid4(), operation="search", knowledge_space=space, required_scopes={Scope.SEARCH}
|
|
)
|
|
grant = workspace.grant(
|
|
id=uuid4(), application=application, knowledge_space=space, role=Role.VIEWER
|
|
)
|
|
|
|
assert application.workspace_id == workspace.id
|
|
assert service_account.application_id == application.id
|
|
assert service_account.workspace_id == workspace.id
|
|
assert policy.knowledge_space_id == space.id
|
|
assert grant.knowledge_space_id == space.id
|
|
|
|
|
|
def test_service_account_cannot_escalate_application_scopes() -> None:
|
|
workspace = Workspace(uuid4(), "Platform", "platform")
|
|
application = workspace.create_application(id=uuid4(), name="Search", scopes={Scope.SEARCH})
|
|
|
|
with pytest.raises(InvalidEntity, match="scopes"):
|
|
application.create_service_account(
|
|
id=uuid4(), name="overreach", scopes={Scope.SEARCH, Scope.ANSWER}
|
|
)
|
|
|
|
|
|
def test_cross_workspace_grant_is_rejected() -> None:
|
|
first = Workspace(uuid4(), "First", "first")
|
|
second = Workspace(uuid4(), "Second", "second")
|
|
application = first.create_application(id=uuid4(), name="Client", scopes={Scope.SEARCH})
|
|
space = second.create_knowledge_space(id=uuid4(), name="Private", slug="private")
|
|
|
|
with pytest.raises(CrossWorkspaceRelationship, match="same workspace"):
|
|
first.grant(id=uuid4(), application=application, knowledge_space=space, role=Role.VIEWER)
|
|
|
|
|
|
@pytest.mark.parametrize("slug", ["Upper", "with space", "-leading", "trailing-"])
|
|
def test_workspace_and_space_slugs_are_normalized_by_contract(slug: str) -> None:
|
|
with pytest.raises(InvalidEntity, match="slug"):
|
|
Workspace(uuid4(), "Invalid", slug)
|
|
|
|
|
|
def test_retention_policy_must_be_positive() -> None:
|
|
workspace = Workspace(uuid4(), "Platform", "platform")
|
|
|
|
with pytest.raises(InvalidEntity, match="retention_days"):
|
|
workspace.create_knowledge_space(
|
|
id=uuid4(), name="Runbooks", slug="runbooks", retention_days=0
|
|
)
|