Files
ACORE/apps/projects/tests.py
T
lokses 030bbc98fd projects: модуль проектов с шагами по отделам и Task.
Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 16:26:45 +03:00

191 lines
7.0 KiB
Python

"""Тесты модуля проектов: шаблон → Task, прогресс, видимость, permission."""
from __future__ import annotations
import datetime
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from apps.employees.models import Employee
from apps.projects.models import (
Project, ProjectStep, ProjectStepItem,
ProjectTemplate, ProjectTemplateStep, ProjectTemplateStepItem,
)
from apps.projects.services import (
activate_project, create_manual_project, create_project_from_template,
)
from apps.tasks.models import Task
from core.models import Directory, DirectoryType, PortalAccess
from core.test_utils import login_2fa
def _grant_projects(user):
ct = ContentType.objects.get_for_model(PortalAccess, for_concrete_model=False)
perm, _ = Permission.objects.get_or_create(
content_type=ct, codename='access_projects',
defaults={'name': 'Доступ к разделу: Проекты'},
)
user.user_permissions.add(perm)
class ProjectServicesTests(TestCase):
@classmethod
def setUpTestData(cls):
dtype = DirectoryType.objects.create(code='department', name='Відділ')
cls.dept_it = Directory.objects.create(
directory_type=dtype, code='it', name='IT',
)
cls.dept_hr = Directory.objects.create(
directory_type=dtype, code='hr', name='HR',
)
common = dict(
mattermost_sync_enabled=False, ad_sync_enabled=False,
hire_date=datetime.date(2020, 1, 1),
user_location_type='office',
)
cls.owner = Employee.objects.create_user(
username='proj_owner', department=cls.dept_it, **common,
)
cls.head = Employee.objects.create_user(
username='proj_head', department=cls.dept_it, **common,
)
cls.peer = Employee.objects.create_user(
username='proj_peer', department=cls.dept_it, manager=cls.head, **common,
)
# head is manager of peer → department_head heuristic
cls.outsider = Employee.objects.create_user(
username='proj_out', department=cls.dept_hr, **common,
)
cls.tpl = ProjectTemplate.objects.create(
name='Открытие аптеки', default_duration_days=90,
)
s1 = ProjectTemplateStep.objects.create(
template=cls.tpl, title='IT', order=0,
department=cls.dept_it, offset_days=0, duration_days=14,
)
ProjectTemplateStepItem.objects.create(
step=s1, title='Настроить сеть', order=0,
)
ProjectTemplateStepItem.objects.create(
step=s1, title='Установить кассу', order=1,
)
s2 = ProjectTemplateStep.objects.create(
template=cls.tpl, title='HR', order=1,
department=cls.dept_hr, offset_days=7, duration_days=21,
)
ProjectTemplateStepItem.objects.create(
step=s2, title='Нанять персонал', order=0,
)
def test_instantiate_template_creates_steps_and_tasks_on_activate(self):
project = create_project_from_template(
template=self.tpl,
title='Аптека тест',
owner=self.owner,
start_date=datetime.date(2026, 1, 1),
)
self.assertEqual(project.status, Project.STATUS_DRAFT)
self.assertEqual(project.steps.count(), 2)
self.assertEqual(
ProjectStepItem.objects.filter(step__project=project).count(), 3,
)
self.assertEqual(
ProjectStepItem.objects.filter(step__project=project, task__isnull=False).count(),
0,
)
activate_project(project)
project.refresh_from_db()
self.assertEqual(project.status, Project.STATUS_ACTIVE)
items = list(
ProjectStepItem.objects.filter(step__project=project).select_related('task')
)
self.assertEqual(len(items), 3)
self.assertTrue(all(i.task_id for i in items))
self.assertEqual(Task.objects.filter(pk__in=[i.task_id for i in items]).count(), 3)
def test_progress_and_auto_done(self):
project = create_project_from_template(
template=self.tpl, title='P2', owner=self.owner,
)
activate_project(project)
items = list(
ProjectStepItem.objects.filter(step__project=project).select_related('task')
)
self.assertEqual(project.progress_percent, 0)
for item in items:
item.task.set_status('done')
project.refresh_from_db()
self.assertEqual(project.progress_percent, 100)
self.assertEqual(project.status, Project.STATUS_DONE)
for step in project.steps.all():
self.assertEqual(step.status, ProjectStep.STATUS_DONE)
def test_for_user_visibility(self):
project = create_project_from_template(
template=self.tpl, title='Vis', owner=self.owner,
)
activate_project(project)
self.assertTrue(
Project.objects.for_user(self.owner).filter(pk=project.pk).exists()
)
# peer in IT department of a step
self.assertTrue(
Project.objects.for_user(self.peer).filter(pk=project.pk).exists()
)
# outsider HR department also has a step
self.assertTrue(
Project.objects.for_user(self.outsider).filter(pk=project.pk).exists()
)
stranger = Employee.objects.create_user(
username='stranger',
mattermost_sync_enabled=False, ad_sync_enabled=False,
hire_date=datetime.date(2020, 1, 1),
user_location_type='office',
)
self.assertFalse(
Project.objects.for_user(stranger).filter(pk=project.pk).exists()
)
def test_manual_create(self):
project = create_manual_project(
title='Ручной',
owner=self.owner,
steps=[{
'title': 'Шаг 1',
'department_id': self.dept_it.pk,
'items': [{'title': 'A'}, {'title': 'B'}],
}],
)
self.assertEqual(project.steps.count(), 1)
self.assertEqual(project.steps.first().items.count(), 2)
class ProjectPortalAccessTests(TestCase):
@classmethod
def setUpTestData(cls):
common = dict(
mattermost_sync_enabled=False, ad_sync_enabled=False,
hire_date=datetime.date(2020, 1, 1),
)
cls.user = Employee.objects.create_user(username='proj_u', **common)
def setUp(self):
self.client = Client()
def test_list_requires_permission(self):
login_2fa(self.client, self.user)
r = self.client.get('/projects/')
self.assertIn(r.status_code, (302, 403))
_grant_projects(self.user)
# clear perm cache
self.user = Employee.objects.get(pk=self.user.pk)
login_2fa(self.client, self.user)
r = self.client.get('/projects/')
self.assertEqual(r.status_code, 200)