| """ |
| Tests for the access control app β Phase 3. |
| |
| Covers role/permission CRUD, roleβpermission binding, |
| userβrole assignment, permission checking, and seeding. |
| """ |
| from django.test import TestCase |
| from django.urls import reverse |
|
|
| from apps.accounts.services import register_user |
| from apps.access.models import Permission, Role, RolePermission, UserRole |
| from apps.access.services import ( |
| assign_permission_to_role, |
| assign_role, |
| check_access, |
| create_permission, |
| create_role, |
| delete_role, |
| get_role, |
| get_role_permissions, |
| get_user_permissions, |
| get_user_roles, |
| has_permission, |
| has_role, |
| list_roles, |
| revoke_permission_from_role, |
| revoke_role, |
| seed_roles_and_permissions, |
| ) |
| from core.exceptions import NotFoundError, ValidationError |
|
|
|
|
| |
|
|
|
|
| class RoleTests(TestCase): |
| """Test role CRUD operations.""" |
|
|
| def test_create_role(self): |
| """Create a new role.""" |
| role = create_role("test_admin", Role.Scope.PLATFORM, "Test admin role.") |
| self.assertEqual(role.name, "test_admin") |
| self.assertEqual(role.scope, Role.Scope.PLATFORM) |
|
|
| def test_create_duplicate_role_fails(self): |
| """Cannot create a role with duplicate name.""" |
| create_role("admin") |
| with self.assertRaises(ValidationError): |
| create_role("admin") |
|
|
| def test_get_role(self): |
| """Retrieve a role by name.""" |
| create_role("viewer") |
| role = get_role("viewer") |
| self.assertEqual(role.name, "viewer") |
|
|
| def test_get_nonexistent_role_fails(self): |
| """Getting a non-existent role raises NotFoundError.""" |
| with self.assertRaises(NotFoundError): |
| get_role("nonexistent") |
|
|
| def test_delete_role(self): |
| """Delete a role removes it from the database.""" |
| create_role("temp") |
| delete_role("temp") |
| with self.assertRaises(NotFoundError): |
| get_role("temp") |
|
|
| def test_list_roles_by_scope(self): |
| """List roles filtered by scope.""" |
| create_role("platform_a", Role.Scope.PLATFORM) |
| create_role("org_a", Role.Scope.ORG) |
| platform_roles = list_roles(scope=Role.Scope.PLATFORM) |
| self.assertTrue(all(r.scope == Role.Scope.PLATFORM for r in platform_roles)) |
|
|
|
|
| |
|
|
|
|
| class PermissionTests(TestCase): |
| """Test permission CRUD.""" |
|
|
| def test_create_permission(self): |
| """Create a new permission.""" |
| perm = create_permission("test_users", "read", "View users") |
| self.assertEqual(perm.codename, "test_users:read") |
|
|
| def test_create_duplicate_permission_fails(self): |
| """Cannot create duplicate permission.""" |
| create_permission("test_users", "read") |
| with self.assertRaises(ValidationError): |
| create_permission("test_users", "read") |
|
|
|
|
| |
|
|
|
|
| class RolePermissionTests(TestCase): |
| """Test role-permission binding.""" |
|
|
| def setUp(self): |
| self.role = create_role("editor", Role.Scope.ORG) |
| self.perm1 = create_permission("articles", "read") |
| self.perm2 = create_permission("articles", "write") |
|
|
| def test_assign_permission_to_role(self): |
| """Assign a permission to a role.""" |
| assign_permission_to_role("editor", "articles", "read") |
| perms = get_role_permissions("editor") |
| self.assertIn("articles:read", perms) |
|
|
| def test_revoke_permission_from_role(self): |
| """Revoke a permission from a role.""" |
| assign_permission_to_role("editor", "articles", "read") |
| revoke_permission_from_role("editor", "articles", "read") |
| perms = get_role_permissions("editor") |
| self.assertNotIn("articles:read", perms) |
|
|
| def test_multiple_permissions_on_role(self): |
| """Role can have multiple permissions.""" |
| assign_permission_to_role("editor", "articles", "read") |
| assign_permission_to_role("editor", "articles", "write") |
| perms = get_role_permissions("editor") |
| self.assertEqual(len(perms), 2) |
|
|
|
|
| |
|
|
|
|
| class UserRoleTests(TestCase): |
| """Test user-role assignment and permission checking.""" |
|
|
| def setUp(self): |
| self.user = register_user("rbac@example.com", "StrongPass123!", org_name="Test Org", business_type="SaaS") |
| self.role = create_role("test_platform_admin", Role.Scope.PLATFORM) |
| create_permission("test_usersxyz", "read") |
| create_permission("test_usersxyz", "write") |
| assign_permission_to_role("test_platform_admin", "test_usersxyz", "read") |
| assign_permission_to_role("test_platform_admin", "test_usersxyz", "write") |
|
|
| def test_assign_role_to_user(self): |
| """Assign a role to a user.""" |
| assign_role(self.user, "test_platform_admin") |
| self.assertTrue(has_role(self.user, "test_platform_admin")) |
|
|
| def test_revoke_role_from_user(self): |
| """Revoke a role from a user.""" |
| assign_role(self.user, "test_platform_admin") |
| revoke_role(self.user, "test_platform_admin") |
| self.assertFalse(has_role(self.user, "test_platform_admin")) |
|
|
| def test_get_user_roles(self): |
| """Get all roles for a user.""" |
| assign_role(self.user, "test_platform_admin") |
| roles = get_user_roles(self.user) |
| self.assertIn("test_platform_admin", roles) |
|
|
| def test_has_permission_via_role(self): |
| """User has permission through assigned role.""" |
| assign_role(self.user, "test_platform_admin") |
| self.assertTrue(has_permission(self.user, "test_usersxyz", "read")) |
| self.assertTrue(has_permission(self.user, "test_usersxyz", "write")) |
|
|
| def test_no_permission_without_role(self): |
| """User without role has no permissions.""" |
| self.assertFalse(has_permission(self.user, "test_usersxyz", "read")) |
|
|
| def test_check_access_codename_format(self): |
| """check_access works with 'resource:action' format.""" |
| assign_role(self.user, "test_platform_admin") |
| self.assertTrue(check_access(self.user, "test_usersxyz:read")) |
| self.assertFalse(check_access(self.user, "billing:read")) |
|
|
| def test_get_user_permissions_set(self): |
| """get_user_permissions returns full set.""" |
| assign_role(self.user, "test_platform_admin") |
| perms = get_user_permissions(self.user) |
| self.assertIn("test_usersxyz:read", perms) |
| self.assertIn("test_usersxyz:write", perms) |
|
|
|
|
| |
|
|
|
|
| class SeedTests(TestCase): |
| """Test role/permission seeding.""" |
|
|
| def test_seed_creates_defaults(self): |
| """Seeding creates default roles and permissions.""" |
| seed_roles_and_permissions() |
| from apps.access.models import Role, Permission |
| self.assertGreater(Role.objects.count(), 0) |
| self.assertGreater(Permission.objects.count(), 0) |
|
|
| def test_seed_is_idempotent(self): |
| """Running seed twice doesn't duplicate records.""" |
| seed_roles_and_permissions() |
| result = seed_roles_and_permissions() |
| self.assertEqual(result["roles_created"], 0) |
| self.assertEqual(result["permissions_created"], 0) |
|
|
|
|
| |
|
|
|
|
| class AccessURLTests(TestCase): |
| """Test URL routing.""" |
|
|
| def test_roles_list_url(self): |
| url = reverse("access:roles-list") |
| self.assertEqual(url, "/access/roles/") |
|
|
| def test_permissions_list_url(self): |
| url = reverse("access:permissions-list") |
| self.assertEqual(url, "/access/permissions/") |
|
|
| def test_my_roles_url(self): |
| url = reverse("access:my-roles") |
| self.assertEqual(url, "/access/my-roles/") |
|
|