Files
tecvico-website/apps/projects/tests/test_views.py
T
2026-07-19 14:31:28 +03:30

213 lines
9.1 KiB
Python

from django.contrib.auth.models import User
from django.core import mail
from django.test import TestCase, override_settings
from django.urls import reverse
from apps.accounts.models import ClientProfile
from apps.projects.models import ProjectBrief, ProjectInternalNote
class ProjectPortalTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username="client",
email="client@example.com",
password="Str0ngPass!word",
first_name="Client",
last_name="User",
)
ClientProfile.objects.create(user=self.user, timezone="UTC")
self.other = User.objects.create_user(
username="other",
email="other@example.com",
password="Str0ngPass!word",
)
ClientProfile.objects.create(user=self.other, timezone="UTC")
def _brief_payload(self):
return {
"title": "Radiomics pipeline",
"category": ProjectBrief.CATEGORY_MEDICAL_IMAGING,
"description": "Need a standardized radiomics workflow.",
"budget_range": ProjectBrief.BUDGET_5K_15K,
"desired_deadline": "2026-12-31",
"reference_links": "https://example.com/spec",
}
def _create_brief(self, **overrides):
defaults = dict(
client=self.user,
title="Brief",
category=ProjectBrief.CATEGORY_WEB,
description="d",
budget_range=ProjectBrief.BUDGET_UNDER_5K,
)
defaults.update(overrides)
return ProjectBrief.objects.create(**defaults)
def test_dashboard_requires_login(self):
response = self.client.get(reverse("projects:dashboard"))
self.assertEqual(response.status_code, 302)
def test_create_brief_sends_admin_email(self):
self.client.login(username="client", password="Str0ngPass!word")
with override_settings(MANAGED_PROJECTS_NOTIFY_EMAILS="ops@tecvico.com"):
response = self.client.post(reverse("projects:create"), data=self._brief_payload())
self.assertEqual(response.status_code, 302)
brief = ProjectBrief.objects.get(title="Radiomics pipeline")
self.assertEqual(brief.client, self.user)
self.assertEqual(brief.status, ProjectBrief.STATUS_SUBMITTED)
self.assertEqual(len(mail.outbox), 1)
self.assertIn("Radiomics pipeline", mail.outbox[0].subject)
self.assertIn("ops@tecvico.com", mail.outbox[0].to)
def test_create_brief_with_title_only(self):
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.post(
reverse("projects:create"),
data={"title": "Minimal brief"},
)
self.assertEqual(response.status_code, 302)
brief = ProjectBrief.objects.get(title="Minimal brief")
self.assertEqual(brief.description, "")
self.assertEqual(brief.category, "")
self.assertEqual(brief.budget_range, "")
def test_create_brief_requires_title(self):
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.post(reverse("projects:create"), data={"title": ""})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "This field is required")
self.assertEqual(ProjectBrief.objects.count(), 0)
def test_create_renders_submit_only_on_last_step(self):
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:create"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'data-wizard-submit hidden')
self.assertContains(response, 'data-wizard-next')
self.assertNotContains(response, 'data-wizard-submit">{{ submit_label }}</button>')
def test_dashboard_lists_only_own_briefs(self):
self._create_brief(title="Mine")
self._create_brief(client=self.other, title="Not mine")
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:dashboard"))
self.assertEqual(response.status_code, 200)
titles = [brief.title for brief in response.context["briefs"]]
self.assertEqual(titles, ["Mine"])
def test_edit_allowed_only_when_submitted(self):
brief = self._create_brief(
title="Editable",
category=ProjectBrief.CATEGORY_DATA,
budget_range=ProjectBrief.BUDGET_NOT_SURE,
status=ProjectBrief.STATUS_SUBMITTED,
)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:edit", kwargs={"pk": brief.pk}))
self.assertEqual(response.status_code, 200)
brief.status = ProjectBrief.STATUS_UNDER_REVIEW
brief.save()
response = self.client.get(reverse("projects:edit", kwargs={"pk": brief.pk}))
self.assertEqual(response.status_code, 302)
def test_locked_project_blocks_edit(self):
brief = self._create_brief(
title="Locked",
status=ProjectBrief.STATUS_SUBMITTED,
locked=True,
)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:edit", kwargs={"pk": brief.pk}))
self.assertEqual(response.status_code, 302)
def test_locked_field_is_disabled_on_edit_form(self):
brief = self._create_brief(
title="Lock field",
status=ProjectBrief.STATUS_SUBMITTED,
lock_title=True,
)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:edit", kwargs={"pk": brief.pk}))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'name="title"')
self.assertContains(response, "disabled")
def test_locked_field_value_is_preserved_on_submit(self):
brief = self._create_brief(
title="Original title",
status=ProjectBrief.STATUS_SUBMITTED,
lock_title=True,
)
self.client.login(username="client", password="Str0ngPass!word")
self.client.post(
reverse("projects:edit", kwargs={"pk": brief.pk}),
data={
"title": "Hacked title",
"category": brief.category,
"description": brief.description,
"budget_range": brief.budget_range,
"desired_deadline": "",
"reference_links": "",
},
)
brief.refresh_from_db()
self.assertEqual(brief.title, "Original title")
def test_status_change_emails_client(self):
brief = self._create_brief(
title="Notify me",
category=ProjectBrief.CATEGORY_RESEARCH,
budget_range=ProjectBrief.BUDGET_15K_50K,
status=ProjectBrief.STATUS_SUBMITTED,
)
mail.outbox.clear()
brief.status = ProjectBrief.STATUS_UNDER_REVIEW
brief.save()
self.assertEqual(len(mail.outbox), 1)
self.assertIn("client@example.com", mail.outbox[0].to)
def test_internal_notes_not_on_detail_page(self):
brief = self._create_brief(
title="Secret notes",
category=ProjectBrief.CATEGORY_OTHER,
)
ProjectInternalNote.objects.create(brief=brief, note="Internal only", author=self.other)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:detail", kwargs={"pk": brief.pk}))
self.assertEqual(response.status_code, 200)
self.assertNotIn(b"Internal only", response.content)
def test_website_notes_visible_on_detail_page(self):
brief = self._create_brief(
title="Public notes",
category=ProjectBrief.CATEGORY_WEB,
website_notes="Your kickoff call is scheduled for next week.",
)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:detail", kwargs={"pk": brief.pk}))
self.assertContains(response, "Notes from Tecvico")
self.assertContains(response, "kickoff call is scheduled")
def test_create_renders_wizard_steps(self):
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:create"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "data-brief-wizard")
self.assertContains(response, "Project basics")
self.assertContains(response, "References &amp; files")
def test_quote_visible_when_quoted(self):
brief = self._create_brief(
title="Quoted project",
category=ProjectBrief.CATEGORY_WORKFLOW,
budget_range=ProjectBrief.BUDGET_50K_PLUS,
status=ProjectBrief.STATUS_QUOTED,
quote_text="We can deliver in 8 weeks for $45,000.",
)
self.client.login(username="client", password="Str0ngPass!word")
response = self.client.get(reverse("projects:detail", kwargs={"pk": brief.pk}))
self.assertContains(response, "We can deliver in 8 weeks")