030bbc98fd
Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media. Co-authored-by: Cursor <cursoragent@cursor.com>
191 lines
7.0 KiB
Python
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)
|