chore: consolidate platform split checks
Some checks failed
Dependency Audit / dependency-audit (push) Has been cancelled
Module Matrix / module-matrix (push) Has been cancelled

This commit is contained in:
2026-07-10 12:51:19 +02:00
parent 150b720f12
commit 635d25c74c
216 changed files with 23336 additions and 4077 deletions

View File

@@ -6,10 +6,10 @@ import unittest
from pathlib import Path
from types import SimpleNamespace
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient
from govoplan_access.backend.auth.dependencies import ApiPrincipal, get_api_principal
from govoplan_access.auth import ApiPrincipal, get_api_principal
from govoplan_core.core.access import (
ACCESS_CAPABILITY_NAMES,
ACCESS_MODULE_ID,
@@ -19,32 +19,41 @@ from govoplan_core.core.access import (
CAPABILITY_ACCESS_PERMISSION_EVALUATOR,
CAPABILITY_ACCESS_PRINCIPAL_RESOLVER,
CAPABILITY_ACCESS_RESOURCE_ACCESS,
CAPABILITY_ACCESS_EXPLANATION,
CAPABILITY_ACCESS_SEMANTIC_DIRECTORY,
CAPABILITY_ACCESS_TENANT_PROVISIONER,
CAPABILITY_AUDIT_SINK,
CAPABILITY_SECURITY_SECRET_PROVIDER,
CAPABILITY_TENANCY_TENANT_RESOLVER,
AccessDecision,
AccessAdministration,
AccessDirectory,
AccessDecisionProvenance,
AccessExplanationService,
AccessGovernanceMaterializer,
AccessSemanticDirectory,
AccessSubjectRef,
AccountRef,
AuditEvent,
AuditSink,
FunctionAssignmentRef,
FunctionDelegationRef,
FunctionRef,
GovernanceTemplateMaterialization,
GroupRef,
IdentityRef,
OrganizationUnitRef,
PermissionEvaluator,
PrincipalRef,
PrincipalResolver,
ResourceAccessService,
RoleRef,
SecretProvider,
TenantRef,
TenantAccessProvisioner,
TenantOwnerCandidateRef,
TenantResolver,
UserRef,
)
from govoplan_core.security.secrets import CAPABILITY_SECURITY_SECRET_PROVIDER, SecretProvider
from govoplan_core.core.campaigns import (
CAPABILITY_CAMPAIGNS_ACCESS,
CAPABILITY_CAMPAIGNS_DELIVERY_TASKS,
@@ -62,11 +71,23 @@ from govoplan_core.core.campaigns import (
from govoplan_core.core.modules import ModuleContext, ModuleManifest
from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.db.base import Base
from govoplan_core.db.session import configure_database, get_database
from govoplan_core.server.app import create_app
from govoplan_core.server.config import GovoplanServerConfig
from govoplan_access.backend.db.models import Account, Group, User, UserGroupMembership
from govoplan_access.backend.db.models import (
Account,
Function,
FunctionAssignment,
FunctionRoleAssignment,
Group,
Identity,
IdentityAccountLink,
OrganizationUnit,
Role,
User,
UserGroupMembership,
)
from govoplan_tenancy.backend.db.models import Tenant
from tests.db_isolation import temporary_database
class _FakeAccessDirectory:
@@ -108,6 +129,64 @@ class _FakeAccessDirectory:
return subject.label
class _FakeAccessSemanticDirectory(_FakeAccessDirectory):
identity = IdentityRef(
id="identity-1",
display_name="Demo Person",
primary_account_id=_FakeAccessDirectory.account.id,
account_ids=(_FakeAccessDirectory.account.id, "account-2"),
)
organization_unit = OrganizationUnitRef(id="ou-1", tenant_id="tenant-1", name="Registry Office")
function = FunctionRef(
id="function-1",
tenant_id="tenant-1",
organization_unit_id=organization_unit.id,
slug="registry-clerk",
name="Registry Clerk",
role_ids=("role-1",),
delegable=True,
act_in_place_allowed=True,
)
assignment = FunctionAssignmentRef(
id="assignment-1",
tenant_id="tenant-1",
account_id=_FakeAccessDirectory.account.id,
identity_id=identity.id,
function_id=function.id,
organization_unit_id=organization_unit.id,
applies_to_subunits=True,
)
def get_identity(self, identity_id: str) -> IdentityRef | None:
return self.identity if identity_id == self.identity.id else None
def accounts_for_identity(self, identity_id: str):
return (self.account,) if identity_id == self.identity.id else ()
def get_organization_unit(self, organization_unit_id: str) -> OrganizationUnitRef | None:
return self.organization_unit if organization_unit_id == self.organization_unit.id else None
def organization_units_for_tenant(self, tenant_id: str):
return (self.organization_unit,) if tenant_id == self.organization_unit.tenant_id else ()
def get_function(self, function_id: str) -> FunctionRef | None:
return self.function if function_id == self.function.id else None
def functions_for_organization_unit(self, organization_unit_id: str, *, include_subunits: bool = False):
del include_subunits
return (self.function,) if organization_unit_id == self.organization_unit.id else ()
def get_function_assignment(self, assignment_id: str) -> FunctionAssignmentRef | None:
return self.assignment if assignment_id == self.assignment.id else None
def function_assignments_for_account(self, account_id: str, *, tenant_id: str | None = None):
if account_id != self.account.id:
return ()
if tenant_id is not None and tenant_id != self.assignment.tenant_id:
return ()
return (self.assignment,)
class _FakePrincipalResolver:
def resolve_request(self, request: object, *, session: object | None = None) -> PrincipalRef:
del request, session
@@ -148,6 +227,30 @@ class _FakeResourceAccessService:
return AccessDecision(allowed=True)
class _FakeAccessExplanationService:
provenance = (
AccessDecisionProvenance(kind="identity", id="identity-1", label="Demo Person"),
AccessDecisionProvenance(kind="function", id="assignment-1", label="Registry Clerk", tenant_id="tenant-1"),
AccessDecisionProvenance(kind="role", id="role-1", label="Clerk", tenant_id="tenant-1"),
AccessDecisionProvenance(kind="right", id="files:file:read", label="Read files"),
)
def explain_scope_provenance(self, principal: PrincipalRef, required_scope: str):
del principal, required_scope
return self.provenance
def explain_resource_provenance(
self,
principal: PrincipalRef,
*,
resource_type: str,
resource_id: str,
action: str,
):
del principal, resource_type, resource_id, action
return self.provenance
class _FakeTenantAccessProvisioner:
def ensure_default_roles(self, session: object, tenant: object | None = None):
del session, tenant
@@ -311,6 +414,8 @@ class AccessContractTests(unittest.TestCase):
self.assertEqual("access.directory", CAPABILITY_ACCESS_DIRECTORY)
self.assertEqual("access.permissionEvaluator", CAPABILITY_ACCESS_PERMISSION_EVALUATOR)
self.assertEqual("access.resourceAccess", CAPABILITY_ACCESS_RESOURCE_ACCESS)
self.assertEqual("access.semanticDirectory", CAPABILITY_ACCESS_SEMANTIC_DIRECTORY)
self.assertEqual("access.explanation", CAPABILITY_ACCESS_EXPLANATION)
self.assertEqual("access.tenantProvisioner", CAPABILITY_ACCESS_TENANT_PROVISIONER)
self.assertEqual("access.administration", CAPABILITY_ACCESS_ADMINISTRATION)
self.assertEqual("access.governanceMaterializer", CAPABILITY_ACCESS_GOVERNANCE_MATERIALIZER)
@@ -329,8 +434,13 @@ class AccessContractTests(unittest.TestCase):
account_id="account-1",
membership_id="user-1",
tenant_id="tenant-1",
identity_id="identity-1",
scopes=frozenset({"campaigns:campaign:read"}),
group_ids=frozenset({"group-1"}),
role_ids=frozenset({"role-1"}),
function_assignment_ids=frozenset({"assignment-1"}),
delegation_ids=frozenset({"delegation-1"}),
acting_for_account_id="account-2",
email="demo@example.test",
)
role = RoleRef(id="role-1", name="Reviewer", level="tenant", tenant_id="tenant-1")
@@ -338,15 +448,80 @@ class AccessContractTests(unittest.TestCase):
self.assertEqual("account-1", principal.account_id)
self.assertEqual(frozenset({"campaigns:campaign:read"}), principal.scopes)
self.assertEqual("Reviewer", role.name)
payload = principal.to_dict()
self.assertEqual(["campaigns:campaign:read"], payload["scopes"])
self.assertEqual(["group-1"], payload["group_ids"])
self.assertEqual(["role-1"], payload["role_ids"])
self.assertEqual(["assignment-1"], payload["function_assignment_ids"])
self.assertEqual(["delegation-1"], payload["delegation_ids"])
self.assertEqual("identity-1", payload["identity_id"])
self.assertEqual("account-2", payload["acting_for_account_id"])
self.assertEqual("session", payload["auth_method"])
self.assertIsNone(payload["api_key_id"])
self.assertIsNone(payload["service_account_id"])
self.assertEqual(principal, PrincipalRef.from_mapping(payload))
with self.assertRaises(AttributeError):
principal.account_id = "changed" # type: ignore[misc]
def test_identity_function_and_delegation_refs_model_organization_scope(self) -> None:
identity = IdentityRef(
id="identity-1",
display_name="Demo Person",
primary_account_id="account-1",
account_ids=("account-1", "account-2"),
)
organization_unit = OrganizationUnitRef(id="ou-1", tenant_id="tenant-1", name="Registry Office")
function = FunctionRef(
id="function-1",
tenant_id="tenant-1",
organization_unit_id=organization_unit.id,
slug="registry-clerk",
name="Registry Clerk",
role_ids=("role-1",),
delegable=True,
act_in_place_allowed=True,
)
assignment = FunctionAssignmentRef(
id="assignment-1",
tenant_id="tenant-1",
account_id="account-1",
identity_id=identity.id,
function_id=function.id,
organization_unit_id=organization_unit.id,
applies_to_subunits=True,
)
delegation = FunctionDelegationRef(
id="delegation-1",
tenant_id="tenant-1",
function_assignment_id=assignment.id,
delegator_account_id="account-1",
delegate_account_id="account-2",
mode="act_in_place",
)
provenance = AccessDecisionProvenance(
kind="function",
id=assignment.id,
label=function.name,
tenant_id="tenant-1",
source="delegation",
details={"organization_unit_id": organization_unit.id, "applies_to_subunits": True},
)
self.assertEqual(("account-1", "account-2"), identity.account_ids)
self.assertEqual("ou-1", function.organization_unit_id)
self.assertTrue(assignment.applies_to_subunits)
self.assertEqual("act_in_place", delegation.mode)
self.assertEqual("function", provenance.to_dict()["kind"])
self.assertEqual({"organization_unit_id": "ou-1", "applies_to_subunits": True}, provenance.to_dict()["details"])
def test_protocol_shapes_match_reference_implementations(self) -> None:
self.assertIsInstance(_FakePrincipalResolver(), PrincipalResolver)
self.assertIsInstance(_FakeTenantResolver(), TenantResolver)
self.assertIsInstance(_FakeAccessDirectory(), AccessDirectory)
self.assertIsInstance(_FakeAccessSemanticDirectory(), AccessSemanticDirectory)
self.assertIsInstance(_FakePermissionEvaluator(), PermissionEvaluator)
self.assertIsInstance(_FakeResourceAccessService(), ResourceAccessService)
self.assertIsInstance(_FakeAccessExplanationService(), AccessExplanationService)
self.assertIsInstance(_FakeTenantAccessProvisioner(), TenantAccessProvisioner)
self.assertIsInstance(_FakeAccessAdministration(), AccessAdministration)
self.assertIsInstance(_FakeAccessGovernanceMaterializer(), AccessGovernanceMaterializer)
@@ -389,6 +564,8 @@ class AccessContractTests(unittest.TestCase):
def test_access_capabilities_register_and_resolve_through_platform_registry(self) -> None:
directory = _FakeAccessDirectory()
semantic_directory = _FakeAccessSemanticDirectory()
explanation = _FakeAccessExplanationService()
resolver = _FakePrincipalResolver()
evaluator = _FakePermissionEvaluator()
tenant_resolver = _FakeTenantResolver()
@@ -400,6 +577,8 @@ class AccessContractTests(unittest.TestCase):
version="test",
capability_factories={
CAPABILITY_ACCESS_DIRECTORY: lambda context: directory,
CAPABILITY_ACCESS_SEMANTIC_DIRECTORY: lambda context: semantic_directory,
CAPABILITY_ACCESS_EXPLANATION: lambda context: explanation,
CAPABILITY_ACCESS_PRINCIPAL_RESOLVER: lambda context: resolver,
CAPABILITY_ACCESS_PERMISSION_EVALUATOR: lambda context: evaluator,
CAPABILITY_ACCESS_ADMINISTRATION: lambda context: administration,
@@ -413,6 +592,8 @@ class AccessContractTests(unittest.TestCase):
registry.configure_capability_context(ModuleContext(registry=registry, settings=object()))
self.assertIs(directory, registry.require_capability(CAPABILITY_ACCESS_DIRECTORY))
self.assertIs(semantic_directory, registry.require_capability(CAPABILITY_ACCESS_SEMANTIC_DIRECTORY))
self.assertIs(explanation, registry.require_capability(CAPABILITY_ACCESS_EXPLANATION))
self.assertIs(resolver, registry.require_capability(CAPABILITY_ACCESS_PRINCIPAL_RESOLVER))
self.assertIs(evaluator, registry.require_capability(CAPABILITY_ACCESS_PERMISSION_EVALUATOR))
self.assertIs(administration, registry.require_capability(CAPABILITY_ACCESS_ADMINISTRATION))
@@ -422,50 +603,240 @@ class AccessContractTests(unittest.TestCase):
def test_sql_directory_and_tenant_resolver_return_access_dtos(self) -> None:
root = Path(tempfile.mkdtemp(prefix="govoplan-access-directory-"))
try:
configure_database(f"sqlite:///{root / 'directory.db'}")
database = get_database()
Base.metadata.create_all(bind=database.engine)
with database.session() as session:
tenant = Tenant(id="tenant-directory", slug="directory", name="Directory Tenant", settings={})
account = Account(
id="account-directory",
email="directory@example.test",
normalized_email="directory@example.test",
display_name="Directory User",
with temporary_database(f"sqlite:///{root / 'directory.db'}") as database:
Base.metadata.create_all(bind=database.engine)
with database.session() as session:
tenant = Tenant(id="tenant-directory", slug="directory", name="Directory Tenant", settings={})
account = Account(
id="account-directory",
email="directory@example.test",
normalized_email="directory@example.test",
display_name="Directory User",
)
user = User(
id="user-directory",
tenant_id=tenant.id,
account_id=account.id,
email=account.email,
display_name=None,
settings={},
mail_profile_policy={},
)
group = Group(id="group-directory", tenant_id=tenant.id, slug="review", name="Review", description=None)
membership = UserGroupMembership(tenant_id=tenant.id, user_id=user.id, group_id=group.id)
identity = Identity(id="identity-directory", display_name="Directory Person", source="local")
identity_link = IdentityAccountLink(
identity_id=identity.id,
account_id=account.id,
is_primary=True,
source="local",
)
organization_unit = OrganizationUnit(
id="ou-directory",
tenant_id=tenant.id,
slug="registry",
name="Registry",
)
role = Role(
id="role-directory",
tenant_id=tenant.id,
slug="registry-reader",
name="Registry Reader",
permissions=["files:read"],
)
function = Function(
id="function-directory",
tenant_id=tenant.id,
organization_unit_id=organization_unit.id,
slug="registry-clerk",
name="Registry Clerk",
delegable=True,
)
function_role = FunctionRoleAssignment(tenant_id=tenant.id, function_id=function.id, role_id=role.id)
function_assignment = FunctionAssignment(
id="assignment-directory",
tenant_id=tenant.id,
account_id=account.id,
identity_id=identity.id,
function_id=function.id,
organization_unit_id=organization_unit.id,
applies_to_subunits=True,
)
session.add_all([
tenant,
account,
user,
group,
membership,
identity,
identity_link,
organization_unit,
role,
function,
function_role,
function_assignment,
])
session.commit()
from govoplan_access.backend.directory import SqlAccessDirectory
from govoplan_access.backend.security.sessions import collect_user_scopes
from govoplan_tenancy.backend.capabilities import SqlTenantResolver
directory = SqlAccessDirectory()
tenant_resolver = SqlTenantResolver()
self.assertEqual("directory@example.test", directory.get_account("account-directory").email) # type: ignore[union-attr]
self.assertEqual("Directory User", directory.get_user("user-directory").display_name) # type: ignore[union-attr]
self.assertEqual(["user-directory"], [user.id for user in directory.users_for_tenant("tenant-directory")])
self.assertEqual(["group-directory"], [group.id for group in directory.groups_for_tenant("tenant-directory")])
self.assertEqual(["group-directory"], [group.id for group in directory.groups_for_user("user-directory", tenant_id="tenant-directory")])
self.assertEqual("Review", directory.display_label(AccessSubjectRef(kind="group", id="group-directory", tenant_id="tenant-directory")))
self.assertEqual("Directory Person", directory.get_identity("identity-directory").display_name) # type: ignore[union-attr]
self.assertEqual(["account-directory"], [account.id for account in directory.accounts_for_identity("identity-directory")])
self.assertEqual("Registry", directory.get_organization_unit("ou-directory").name) # type: ignore[union-attr]
self.assertEqual(["role-directory"], list(directory.get_function("function-directory").role_ids)) # type: ignore[union-attr]
self.assertEqual(
["assignment-directory"],
[item.id for item in directory.function_assignments_for_account("account-directory", tenant_id="tenant-directory")],
)
user = User(
id="user-directory",
tenant_id=tenant.id,
account_id=account.id,
email=account.email,
display_name=None,
settings={},
mail_profile_policy={},
with database.session() as session:
user = session.get(User, "user-directory")
self.assertIn("files:read", collect_user_scopes(session, user, include_system=False)) # type: ignore[arg-type]
self.assertEqual("directory", tenant_resolver.get_tenant("tenant-directory").slug) # type: ignore[union-attr]
self.assertEqual(
"tenant-directory",
tenant_resolver.current_tenant(
PrincipalRef(account_id="account-directory", membership_id="user-directory", tenant_id="tenant-directory")
).id, # type: ignore[union-attr]
)
group = Group(id="group-directory", tenant_id=tenant.id, slug="review", name="Review", description=None)
membership = UserGroupMembership(tenant_id=tenant.id, user_id=user.id, group_id=group.id)
session.add_all([tenant, account, user, group, membership])
session.commit()
finally:
shutil.rmtree(root, ignore_errors=True)
from govoplan_access.backend.directory import SqlAccessDirectory
from govoplan_tenancy.backend.capabilities import SqlTenantResolver
def test_semantic_access_admin_crud_endpoints(self) -> None:
root = Path(tempfile.mkdtemp(prefix="govoplan-access-semantic-api-"))
try:
with temporary_database(f"sqlite:///{root / 'semantic.db'}") as database:
Base.metadata.create_all(bind=database.engine)
tenant_id = "tenant-semantic"
account_id = "account-semantic"
delegate_account_id = "account-semantic-delegate"
user_id = "user-semantic"
role_id = "role-semantic"
with database.session() as session:
tenant = Tenant(id=tenant_id, slug="semantic", name="Semantic Tenant", settings={})
account = Account(
id=account_id,
email="semantic@example.test",
normalized_email="semantic@example.test",
display_name="Semantic User",
)
delegate_account = Account(
id=delegate_account_id,
email="delegate@example.test",
normalized_email="delegate@example.test",
display_name="Delegate User",
)
user = User(
id=user_id,
tenant_id=tenant_id,
account_id=account_id,
email=account.email,
display_name=account.display_name,
settings={},
mail_profile_policy={},
)
role = Role(
id=role_id,
tenant_id=tenant_id,
slug="semantic-reader",
name="Semantic Reader",
permissions=["files:read"],
)
session.add_all([tenant, account, delegate_account, user, role])
session.commit()
directory = SqlAccessDirectory()
tenant_resolver = SqlTenantResolver()
from govoplan_access.backend.api.v1.routes import router as admin_router
self.assertEqual("directory@example.test", directory.get_account("account-directory").email) # type: ignore[union-attr]
self.assertEqual("Directory User", directory.get_user("user-directory").display_name) # type: ignore[union-attr]
self.assertEqual(["user-directory"], [user.id for user in directory.users_for_tenant("tenant-directory")])
self.assertEqual(["group-directory"], [group.id for group in directory.groups_for_tenant("tenant-directory")])
self.assertEqual(["group-directory"], [group.id for group in directory.groups_for_user("user-directory", tenant_id="tenant-directory")])
self.assertEqual("Review", directory.display_label(AccessSubjectRef(kind="group", id="group-directory", tenant_id="tenant-directory")))
self.assertEqual("directory", tenant_resolver.get_tenant("tenant-directory").slug) # type: ignore[union-attr]
self.assertEqual(
"tenant-directory",
tenant_resolver.current_tenant(
PrincipalRef(account_id="account-directory", membership_id="user-directory", tenant_id="tenant-directory")
).id, # type: ignore[union-attr]
)
app = FastAPI()
app.include_router(admin_router)
def principal_override() -> ApiPrincipal:
with database.session() as session:
account = session.get(Account, account_id)
user = session.get(User, user_id)
assert account is not None
assert user is not None
return ApiPrincipal(
principal=PrincipalRef(
account_id=account_id,
membership_id=user_id,
tenant_id=tenant_id,
scopes=frozenset({
"admin:roles:read",
"admin:roles:write",
"admin:roles:assign",
"system:accounts:read",
"system:accounts:update",
}),
),
account=account,
user=user,
)
app.dependency_overrides[get_api_principal] = principal_override
with TestClient(app) as client:
identity_response = client.post(
"/admin/identities",
json={"display_name": "Semantic Person", "account_ids": [account_id], "primary_account_id": account_id},
)
self.assertEqual(201, identity_response.status_code, identity_response.text)
identity_id = identity_response.json()["id"]
unit_response = client.post(
"/admin/organization-units",
json={"slug": "registry", "name": "Registry"},
)
self.assertEqual(201, unit_response.status_code, unit_response.text)
organization_unit_id = unit_response.json()["id"]
function_response = client.post(
"/admin/functions",
json={
"organization_unit_id": organization_unit_id,
"slug": "registry-clerk",
"name": "Registry Clerk",
"role_ids": [role_id],
"delegable": True,
},
)
self.assertEqual(201, function_response.status_code, function_response.text)
function_id = function_response.json()["id"]
self.assertEqual([role_id], function_response.json()["role_ids"])
assignment_response = client.post(
"/admin/function-assignments",
json={
"account_id": account_id,
"identity_id": identity_id,
"function_id": function_id,
"applies_to_subunits": True,
},
)
self.assertEqual(201, assignment_response.status_code, assignment_response.text)
assignment_id = assignment_response.json()["id"]
self.assertTrue(assignment_response.json()["applies_to_subunits"])
delegation_response = client.post(
"/admin/function-delegations",
json={"function_assignment_id": assignment_id, "delegate_account_id": delegate_account_id},
)
self.assertEqual(201, delegation_response.status_code, delegation_response.text)
self.assertEqual(delegate_account_id, delegation_response.json()["delegate_account_id"])
list_response = client.get(f"/admin/function-assignments?account_id={account_id}")
self.assertEqual(200, list_response.status_code, list_response.text)
self.assertEqual([assignment_id], [item["id"] for item in list_response.json()["assignments"]])
finally:
shutil.rmtree(root, ignore_errors=True)
@@ -532,32 +903,32 @@ class AccessContractTests(unittest.TestCase):
lifespan=None,
app_configurators=(),
)
app = create_app(config)
with temporary_database(f"sqlite:///{root / 'test.db'}") as database:
app = create_app(config)
database = get_database()
Base.metadata.create_all(bind=database.engine)
with database.session() as session:
tenant = Tenant(id=tenant_id, slug="capability", name="Capability Tenant", settings={})
account = Account(
id=account_id,
email="capability@example.test",
normalized_email="capability@example.test",
display_name="Capability User",
)
user = User(
id=user_id,
tenant_id=tenant_id,
account_id=account_id,
email=account.email,
display_name=account.display_name,
settings={},
mail_profile_policy={},
)
session.add_all([tenant, account, user])
session.commit()
Base.metadata.create_all(bind=database.engine)
with database.session() as session:
tenant = Tenant(id=tenant_id, slug="capability", name="Capability Tenant", settings={})
account = Account(
id=account_id,
email="capability@example.test",
normalized_email="capability@example.test",
display_name="Capability User",
)
user = User(
id=user_id,
tenant_id=tenant_id,
account_id=account_id,
email=account.email,
display_name=account.display_name,
settings={},
mail_profile_policy={},
)
session.add_all([tenant, account, user])
session.commit()
with TestClient(app) as client:
response = client.get("/api/v1/capability-principal")
with TestClient(app) as client:
response = client.get("/api/v1/capability-principal")
self.assertEqual(response.status_code, 200, response.text)
self.assertEqual(