test(BA-5375): add RBAC registry completeness test#10534
test(BA-5375): add RBAC registry completeness test#10534
Conversation
Extract the inline RBAC action registry list from factory.py into a shared rbac_registry.py module, and add tests that verify every concrete BaseRBACAction subclass is registered, with no duplicates and no non-subclass entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Extracts the RBAC action registry into a dedicated module and adds a unit test to ensure all concrete BaseRBACAction implementations are registered so get_entity_valid_operations() can’t silently omit new actions.
Changes:
- Introduced a shared
RBAC_ACTION_REGISTRYmodule for the permission controller wiring. - Updated the service factory to use the shared registry instead of an inline list.
- Added completeness/uniqueness/type-safety tests for the RBAC registry.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tests/unit/manager/actions/test_rbac_registry.py |
Adds tests to validate the RBAC registry is complete, has no duplicates, and contains only valid action subclasses. |
src/ai/backend/manager/services/factory.py |
Switches PermissionControllerService wiring to use the centralized RBAC registry. |
src/ai/backend/manager/actions/action/rbac_registry.py |
New canonical RBAC action registry module exported for reuse and testability. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| def test_registry_contains_only_base_rbac_action_subclasses(self) -> None: | ||
| non_subclasses: list[str] = [] | ||
| for entry in RBAC_ACTION_REGISTRY: |
There was a problem hiding this comment.
issubclass(cls, BaseRBACAction) will raise TypeError if an entry in RBAC_ACTION_REGISTRY is not a class/type (e.g., None, a function, etc.), which makes the test error out instead of reporting a clear assertion failure. Consider first checking isinstance(entry, type) (or catching TypeError) and then treating non-types as invalid registry entries so the failure message is actionable.
| for entry in RBAC_ACTION_REGISTRY: | |
| for entry in RBAC_ACTION_REGISTRY: | |
| if not isinstance(entry, type): | |
| non_subclasses.append(repr(entry)) | |
| continue |
| RBAC_ACTION_REGISTRY: list[type[BaseRBACAction]] = [ | ||
| SessionCreateRBACAction, | ||
| SessionGetRBACAction, | ||
| SessionSearchRBACAction, | ||
| SessionUpdateRBACAction, | ||
| SessionHardDeleteRBACAction, | ||
| SessionGrantAllRBACAction, | ||
| SessionGrantReadRBACAction, | ||
| SessionGrantUpdateRBACAction, | ||
| SessionGrantHardDeleteRBACAction, | ||
| ] |
There was a problem hiding this comment.
RBAC_ACTION_REGISTRY is a module-level constant but is defined as a mutable list. Since factory.create_services() passes this object through directly, any accidental mutation would affect all consumers (and could create hard-to-debug global state). Consider making it immutable (e.g., Final + tuple[...]) and type it as a Sequence[type[BaseRBACAction]]/tuple[...] to communicate intent and prevent mutation.
Summary
factory.pyinto a sharedrbac_registry.pymodule for testabilityTestRBACRegistryCompletenesstests that verify every concreteBaseRBACActionsubclass is registered, with no duplicates and no non-subclass entriesget_entity_valid_operations()API responseTest plan
pants test tests/unit/manager/actions/test_rbac_registry.pypassespants test tests/unit/manager/actions/test_rbac_session.pypasses (existing tests unaffected)pants lint/pants fmt/pants checkclean (only pre-existingprocessors.pymypy error)🤖 Generated with Claude Code