238 lines
8.1 KiB
Python
238 lines
8.1 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
from collections.abc import Mapping, Sequence
|
|
from unittest.mock import patch
|
|
|
|
from govoplan_core.core.modules import ModuleManifest
|
|
from govoplan_core.core.registry import PlatformRegistry
|
|
from govoplan_core.tenancy.service import tenant_counts_many
|
|
|
|
|
|
EXPECTED_EMPTY_COUNTS = {
|
|
"users": 0,
|
|
"active_users": 0,
|
|
"groups": 0,
|
|
"campaigns": 0,
|
|
"files": 0,
|
|
"api_keys": 0,
|
|
"active_api_keys": 0,
|
|
}
|
|
|
|
|
|
class _BatchAccessAdministration:
|
|
def __init__(self) -> None:
|
|
self.calls: list[tuple[str, ...]] = []
|
|
|
|
def tenant_counts_many(
|
|
self,
|
|
session: object,
|
|
tenant_ids: Sequence[str],
|
|
) -> Mapping[str, Mapping[str, int]]:
|
|
del session
|
|
ids = tuple(tenant_ids)
|
|
self.calls.append(ids)
|
|
return {
|
|
tenant_id: {
|
|
"users": index + 1,
|
|
"active_users": index + 1,
|
|
"groups": 1,
|
|
"api_keys": 0,
|
|
"active_api_keys": 0,
|
|
}
|
|
for index, tenant_id in enumerate(ids)
|
|
}
|
|
|
|
|
|
class TenantSummaryBatchingTests(unittest.TestCase):
|
|
def test_absent_providers_return_complete_zero_summaries(self) -> None:
|
|
registry = PlatformRegistry()
|
|
with (
|
|
patch("govoplan_core.tenancy.service.get_registry", return_value=registry),
|
|
patch("govoplan_core.tenancy.service._access_administration", return_value=None),
|
|
):
|
|
result = tenant_counts_many(object(), ["tenant-1", "tenant-2"])
|
|
|
|
self.assertEqual(
|
|
{
|
|
"tenant-1": EXPECTED_EMPTY_COUNTS,
|
|
"tenant-2": EXPECTED_EMPTY_COUNTS,
|
|
},
|
|
result,
|
|
)
|
|
|
|
def test_legacy_single_tenant_provider_remains_supported(self) -> None:
|
|
calls: list[str] = []
|
|
|
|
def single_provider(session: object, tenant_id: str) -> Mapping[str, int]:
|
|
del session
|
|
calls.append(tenant_id)
|
|
return {"campaigns": 1}
|
|
|
|
registry = PlatformRegistry()
|
|
registry.register(
|
|
ModuleManifest(
|
|
id="campaigns",
|
|
name="Campaigns",
|
|
version="test",
|
|
tenant_summary_providers=(single_provider,),
|
|
)
|
|
)
|
|
with (
|
|
patch("govoplan_core.tenancy.service.get_registry", return_value=registry),
|
|
patch("govoplan_core.tenancy.service._access_administration", return_value=None),
|
|
):
|
|
result = tenant_counts_many(
|
|
object(),
|
|
["tenant-1", "tenant-2"],
|
|
module_ids=("campaigns",),
|
|
)
|
|
|
|
self.assertEqual(["tenant-1", "tenant-2"], calls)
|
|
self.assertEqual(1, result["tenant-1"]["campaigns"])
|
|
self.assertEqual(1, result["tenant-2"]["campaigns"])
|
|
|
|
def test_batch_provider_handles_single_item_and_partial_results(self) -> None:
|
|
calls: list[tuple[str, ...]] = []
|
|
|
|
def single_provider(session: object, tenant_id: str) -> Mapping[str, int]:
|
|
raise AssertionError(f"single provider should not run for {tenant_id}")
|
|
|
|
def batch_provider(
|
|
session: object,
|
|
tenant_ids: Sequence[str],
|
|
) -> Mapping[str, Mapping[str, int]]:
|
|
del session
|
|
calls.append(tuple(tenant_ids))
|
|
return {tenant_ids[0]: {"files": 7}}
|
|
|
|
registry = PlatformRegistry()
|
|
registry.register(
|
|
ModuleManifest(
|
|
id="files",
|
|
name="Files",
|
|
version="test",
|
|
tenant_summary_providers=(single_provider,),
|
|
tenant_summary_batch_providers=(batch_provider,),
|
|
)
|
|
)
|
|
with (
|
|
patch("govoplan_core.tenancy.service.get_registry", return_value=registry),
|
|
patch("govoplan_core.tenancy.service._access_administration", return_value=None),
|
|
):
|
|
single = tenant_counts_many(object(), ["tenant-1"], module_ids=("files",))
|
|
partial = tenant_counts_many(
|
|
object(),
|
|
["tenant-1", "tenant-2"],
|
|
module_ids=("files",),
|
|
)
|
|
|
|
self.assertEqual([("tenant-1",), ("tenant-1", "tenant-2")], calls)
|
|
self.assertEqual(7, single["tenant-1"]["files"])
|
|
self.assertEqual(7, partial["tenant-1"]["files"])
|
|
self.assertEqual(0, partial["tenant-2"]["files"])
|
|
|
|
def test_batch_provider_failures_remain_visible(self) -> None:
|
|
def single_provider(session: object, tenant_id: str) -> Mapping[str, int]:
|
|
return {}
|
|
|
|
def failing_provider(
|
|
session: object,
|
|
tenant_ids: Sequence[str],
|
|
) -> Mapping[str, Mapping[str, int]]:
|
|
raise RuntimeError("summary unavailable")
|
|
|
|
registry = PlatformRegistry()
|
|
registry.register(
|
|
ModuleManifest(
|
|
id="files",
|
|
name="Files",
|
|
version="test",
|
|
tenant_summary_providers=(single_provider,),
|
|
tenant_summary_batch_providers=(failing_provider,),
|
|
)
|
|
)
|
|
with (
|
|
patch("govoplan_core.tenancy.service.get_registry", return_value=registry),
|
|
patch("govoplan_core.tenancy.service._access_administration", return_value=None),
|
|
self.assertRaisesRegex(RuntimeError, "summary unavailable"),
|
|
):
|
|
tenant_counts_many(object(), ["tenant-1"], module_ids=("files",))
|
|
|
|
def test_provider_calls_are_bounded_for_large_pages(self) -> None:
|
|
provider_calls = {"campaigns": 0, "files": 0}
|
|
|
|
def single_provider(session: object, tenant_id: str) -> Mapping[str, int]:
|
|
raise AssertionError(f"single provider should not run for {tenant_id}")
|
|
|
|
def batch_provider(module_id: str):
|
|
def provide(
|
|
session: object,
|
|
tenant_ids: Sequence[str],
|
|
) -> Mapping[str, Mapping[str, int]]:
|
|
del session
|
|
provider_calls[module_id] += 1
|
|
return {tenant_id: {} for tenant_id in tenant_ids}
|
|
|
|
return provide
|
|
|
|
registry = PlatformRegistry()
|
|
for module_id in ("campaigns", "files"):
|
|
registry.register(
|
|
ModuleManifest(
|
|
id=module_id,
|
|
name=module_id.title(),
|
|
version="test",
|
|
tenant_summary_providers=(single_provider,),
|
|
tenant_summary_batch_providers=(batch_provider(module_id),),
|
|
)
|
|
)
|
|
access = _BatchAccessAdministration()
|
|
tenant_ids = [f"tenant-{index}" for index in range(100)]
|
|
with (
|
|
patch("govoplan_core.tenancy.service.get_registry", return_value=registry),
|
|
patch("govoplan_core.tenancy.service._access_administration", return_value=access),
|
|
):
|
|
result = tenant_counts_many(
|
|
object(),
|
|
tenant_ids,
|
|
module_ids=("campaigns", "files"),
|
|
)
|
|
|
|
self.assertEqual({"campaigns": 1, "files": 1}, provider_calls)
|
|
self.assertEqual([tuple(tenant_ids)], access.calls)
|
|
self.assertEqual(100, len(result))
|
|
self.assertEqual(100, result["tenant-99"]["users"])
|
|
|
|
def test_registry_replacement_carries_batch_providers(self) -> None:
|
|
def single_provider(session: object, tenant_id: str) -> Mapping[str, int]:
|
|
return {}
|
|
|
|
def batch_provider(
|
|
session: object,
|
|
tenant_ids: Sequence[str],
|
|
) -> Mapping[str, Mapping[str, int]]:
|
|
return {}
|
|
|
|
registry = PlatformRegistry()
|
|
registry.replace(
|
|
[
|
|
ModuleManifest(
|
|
id="files",
|
|
name="Files",
|
|
version="test",
|
|
tenant_summary_providers=(single_provider,),
|
|
tenant_summary_batch_providers=(batch_provider,),
|
|
)
|
|
]
|
|
)
|
|
|
|
self.assertIs(
|
|
batch_provider,
|
|
registry.tenant_summary_batch_providers()["files"],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|