Files
ACORE/core/tests_user_groups.py
lokses 7d319a8331 tests: починка после переименования пути админки
Тесты больше не хардкодят /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>
2026-09-21 23:13:23 +03:00

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)