7d319a8331
Тесты больше не хардкодят /admin/ — путь берётся из reverse (admin:index /
именованный admin-URL), поэтому переименование префикса админки их не ломает.
Плюс подчинённому в EmployeeDetailTests задан email, не содержащий логин —
иначе проверка «логин скрыт от руководителя» ложно срабатывала на подстроку.
- apps/employees/tests: reverse('admin:employees_employee_change')
- core/tests_user_groups: matrix_url через reverse('admin:index')
- core/tests: distinct email у подчинённого
Полный прогон: 560 тестов, 0 провалов.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
211 lines
8.6 KiB
Python
211 lines
8.6 KiB
Python
from datetime import date
|
|
|
|
from django.contrib.auth.models import Group, Permission
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.test import TestCase
|
|
|
|
from apps.employees.models import Employee, GroupSectionAccess
|
|
from core.models import PortalAccess
|
|
from core.portal_access import user_can_access, user_can_section_action
|
|
from core.test_utils import login_2fa
|
|
from core.user_groups_portal import apply_group_access_matrix, copy_group_access
|
|
|
|
|
|
def make_employee(**overrides):
|
|
n = Employee.objects.count() + 1
|
|
defaults = {
|
|
'username': f'ug.user{n}',
|
|
'email': f'ug.user{n}@company.local',
|
|
'first_name': 'Тест',
|
|
'last_name': 'Группы',
|
|
'hire_date': date(2026, 1, 1),
|
|
'user_location_type': 'office',
|
|
'mattermost_sync_enabled': False,
|
|
'ad_sync_enabled': False,
|
|
'is_staff': False,
|
|
}
|
|
defaults.update(overrides)
|
|
return Employee.objects.create(**defaults)
|
|
|
|
|
|
def portal_perm(codename: str) -> Permission:
|
|
ct = ContentType.objects.get_for_model(PortalAccess, for_concrete_model=False)
|
|
return Permission.objects.get(content_type=ct, codename=codename)
|
|
|
|
|
|
def clear_perm_cache(user):
|
|
for attr in ('_perm_cache', '_user_perm_cache', '_group_perm_cache'):
|
|
if hasattr(user, attr):
|
|
delattr(user, attr)
|
|
|
|
|
|
class UserGroupsMatrixTests(TestCase):
|
|
def test_staff_only_section(self):
|
|
user = make_employee()
|
|
self.assertFalse(user_can_access(user, 'user_groups'))
|
|
staff = make_employee(is_staff=True)
|
|
self.assertTrue(user_can_access(staff, 'user_groups'))
|
|
|
|
def test_non_staff_forbidden_on_list(self):
|
|
user = make_employee()
|
|
login_2fa(self.client, user)
|
|
resp = self.client.get('/user-groups/', HTTP_HX_REQUEST='true')
|
|
self.assertEqual(resp.status_code, 302)
|
|
self.assertEqual(resp['Location'], '/dashboard/')
|
|
self.assertEqual(resp['HX-Redirect'], '/dashboard/')
|
|
|
|
def test_staff_can_open_list(self):
|
|
staff = make_employee(is_staff=True)
|
|
login_2fa(self.client, staff)
|
|
resp = self.client.get('/user-groups/', HTTP_HX_REQUEST='true')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
def test_apply_matrix_syncs_view_and_cud(self):
|
|
group = Group.objects.create(name='Matrix Test Group')
|
|
apply_group_access_matrix(group, {
|
|
'view_databases': '1',
|
|
'create_databases': '1',
|
|
'update_databases': '1',
|
|
# delete off
|
|
'view_reports': '0',
|
|
})
|
|
self.assertTrue(group.permissions.filter(codename='access_databases').exists())
|
|
self.assertFalse(group.permissions.filter(codename='access_reports').exists())
|
|
row = GroupSectionAccess.objects.get(group=group, section_key='databases')
|
|
self.assertTrue(row.can_create)
|
|
self.assertTrue(row.can_update)
|
|
self.assertFalse(row.can_delete)
|
|
|
|
apply_group_access_matrix(group, {
|
|
'view_databases': '1',
|
|
'view_all_reports': '1',
|
|
})
|
|
self.assertTrue(group.permissions.filter(codename='view_all_departments').exists())
|
|
apply_group_access_matrix(group, {'view_databases': '1', 'view_all_reports': '0'})
|
|
self.assertFalse(group.permissions.filter(codename='view_all_departments').exists())
|
|
|
|
apply_group_access_matrix(group, {'view_databases': '0'})
|
|
self.assertFalse(group.permissions.filter(codename='access_databases').exists())
|
|
self.assertFalse(
|
|
GroupSectionAccess.objects.filter(group=group, section_key='databases').exists()
|
|
)
|
|
|
|
def test_user_can_section_action(self):
|
|
group = Group.objects.create(name='CUD Group')
|
|
group.permissions.add(portal_perm('access_databases'))
|
|
GroupSectionAccess.objects.create(
|
|
group=group,
|
|
section_key='databases',
|
|
can_create=True,
|
|
can_update=False,
|
|
can_delete=False,
|
|
)
|
|
user = make_employee()
|
|
user.groups.add(group)
|
|
clear_perm_cache(user)
|
|
|
|
self.assertTrue(user_can_access(user, 'databases'))
|
|
self.assertTrue(user_can_section_action(user, 'databases', 'create'))
|
|
self.assertFalse(user_can_section_action(user, 'databases', 'update'))
|
|
self.assertFalse(user_can_section_action(user, 'databases', 'delete'))
|
|
|
|
staff = make_employee(is_staff=True)
|
|
staff.groups.add(group)
|
|
clear_perm_cache(staff)
|
|
# staff не обходит CUD
|
|
self.assertFalse(user_can_section_action(staff, 'databases', 'update'))
|
|
|
|
su = make_employee(is_superuser=True, is_staff=True)
|
|
self.assertTrue(user_can_section_action(su, 'databases', 'update'))
|
|
|
|
def test_copy_access(self):
|
|
src = Group.objects.create(name='Copy Src')
|
|
dst = Group.objects.create(name='Copy Dst')
|
|
apply_group_access_matrix(src, {
|
|
'view_email': '1',
|
|
'create_email': '1',
|
|
'update_email': '0',
|
|
'delete_email': '0',
|
|
'view_all_reports': '1',
|
|
})
|
|
copy_group_access(src, dst)
|
|
self.assertTrue(dst.permissions.filter(codename='access_email').exists())
|
|
self.assertTrue(dst.permissions.filter(codename='view_all_departments').exists())
|
|
row = GroupSectionAccess.objects.get(group=dst, section_key='email')
|
|
self.assertTrue(row.can_create)
|
|
self.assertFalse(row.can_update)
|
|
self.assertFalse(row.can_delete)
|
|
|
|
def test_copy_endpoint(self):
|
|
src = Group.objects.create(name='Copy Endpoint Src')
|
|
dst = Group.objects.create(name='Copy Endpoint Dst')
|
|
apply_group_access_matrix(src, {
|
|
'view_structure': '1',
|
|
'create_structure': '1',
|
|
'update_structure': '1',
|
|
'delete_structure': '1',
|
|
})
|
|
staff = make_employee(is_staff=True)
|
|
login_2fa(self.client, staff)
|
|
resp = self.client.post(
|
|
f'/user-groups/{src.pk}/access/copy/',
|
|
{'target_group_id': str(dst.pk)},
|
|
)
|
|
self.assertEqual(resp.status_code, 302)
|
|
self.assertTrue(dst.permissions.filter(codename='access_structure').exists())
|
|
|
|
def test_individual_view_all_reports_grant_and_revoke(self):
|
|
from apps.reports.it_monthly.builder import user_can_view_all_department_reports
|
|
|
|
staff = make_employee(is_staff=True, last_name='Админ', first_name='Портал', username='ug.staff')
|
|
person = make_employee(last_name='Иванов', first_name='Пётр', username='ivanov.rep')
|
|
self.assertFalse(user_can_view_all_department_reports(person))
|
|
|
|
login_2fa(self.client, staff)
|
|
page = self.client.get('/user-groups/')
|
|
self.assertContains(page, 'Отчёты всех отделов — отдельным сотрудникам')
|
|
resp = self.client.post(
|
|
'/user-groups/individual-reports/',
|
|
{'employee_id': str(person.pk)},
|
|
)
|
|
self.assertEqual(resp.status_code, 302)
|
|
person = Employee.objects.get(pk=person.pk)
|
|
self.assertTrue(user_can_view_all_department_reports(person))
|
|
|
|
listed = self.client.get('/user-groups/')
|
|
self.assertContains(listed, 'ivanov.rep')
|
|
|
|
revoke = self.client.post(f'/user-groups/individual-reports/{person.pk}/revoke/')
|
|
self.assertEqual(revoke.status_code, 302)
|
|
person = Employee.objects.get(pk=person.pk)
|
|
self.assertFalse(user_can_view_all_department_reports(person))
|
|
|
|
|
|
class GroupAccessMatrixAdminTests(TestCase):
|
|
"""Полная сетка доступа в админке (/admin/auth/group/access-matrix/)."""
|
|
|
|
def setUp(self):
|
|
from django.urls import reverse
|
|
self.admin = make_employee(username='ug.admin', is_staff=True, is_superuser=True)
|
|
login_2fa(self.client, self.admin)
|
|
self.group = Group.objects.create(name='Тест-роль')
|
|
# Префикс админки берём из reverse — не зависим от того, как назван путь.
|
|
self.matrix_url = reverse('admin:index') + 'auth/group/access-matrix/'
|
|
|
|
def test_matrix_renders(self):
|
|
resp = self.client.get(self.matrix_url)
|
|
self.assertEqual(resp.status_code, 200)
|
|
self.assertContains(resp, 'Тест-роль')
|
|
|
|
def test_matrix_save_grants_view(self):
|
|
# включаем раздел 'reports' для группы через сетку
|
|
self.client.post(self.matrix_url, {
|
|
f'g{self.group.id}_reports': 'on',
|
|
})
|
|
self.assertTrue(user_group_has_reports_access(self.group))
|
|
|
|
|
|
def user_group_has_reports_access(group):
|
|
from core.user_groups_portal import _group_view_keys
|
|
return 'reports' in _group_view_keys(group)
|