Files
qlockify-backend-deployment/apps/projects/tests/test_views.py

220 lines
8.6 KiB
Python

from decimal import Decimal
from rest_framework.test import APITestCase
from apps.clients.models import Client
from apps.projects.models import Project, ProjectAccess, ProjectUserRate
from apps.users.models import User
from apps.workspaces.models import PriceUnit, Workspace, WorkspaceMembership, WorkspaceUserRate
class ProjectViewTests(APITestCase):
@classmethod
def setUpTestData(cls):
cls.owner = User.objects.create_user(
mobile="09121110001",
password="secret123",
first_name="Owner",
)
cls.workspace = Workspace.objects.create(name="Projects", owner=cls.owner)
PriceUnit.objects.create(code="USD", name="US Dollar", local_name="Dollar", symbol="$")
cls.member = User.objects.create_user(
mobile="09121110002",
password="secret123",
first_name="Member",
)
WorkspaceMembership.objects.create(
workspace=cls.workspace,
user=cls.member,
role=WorkspaceMembership.Role.MEMBER,
is_active=True,
)
cls.first_client = Client.objects.create(workspace=cls.workspace, name="Acme")
cls.second_client = Client.objects.create(workspace=cls.workspace, name="Globex")
cls.third_client = Client.objects.create(workspace=cls.workspace, name="Initech")
Project.objects.create(
workspace=cls.workspace,
client=cls.first_client,
name="Alpha",
)
cls.second_project = Project.objects.create(
workspace=cls.workspace,
client=cls.second_client,
name="Beta",
)
cls.third_project = Project.objects.create(
workspace=cls.workspace,
client=cls.third_client,
name="Gamma",
)
cls.first_project = Project.objects.get(name="Alpha")
ProjectAccess.objects.create(project=cls.first_project, user=cls.member)
ProjectAccess.objects.create(project=cls.second_project, user=cls.member)
WorkspaceUserRate.objects.create(
workspace=cls.workspace,
user=cls.member,
hourly_rate=Decimal("25.00"),
currency="USD",
effective_from=cls.workspace.created_at,
is_active=True,
)
def test_project_list_supports_multi_client_filter(self):
self.client.force_authenticate(user=self.member)
response = self.client.get(
"/api/projects/",
[
("workspace", str(self.workspace.id)),
("clients", str(self.first_client.id)),
("clients", str(self.second_client.id)),
],
)
self.assertEqual(response.status_code, 200)
items = (
response.data
if isinstance(response.data, list)
else response.data.get("results") or response.data.get("items", [])
)
result_ids = {str(item["client"]["id"]) for item in items}
self.assertEqual(
result_ids,
{str(self.first_client.id), str(self.second_client.id)},
)
def test_project_access_list_and_mutations_require_explicit_member_access(self):
self.client.force_authenticate(user=self.owner)
access_response = self.client.get(
"/api/projects/access/",
{"workspace": str(self.workspace.id), "user": str(self.member.id)},
)
self.assertEqual(access_response.status_code, 200)
items = access_response.data["items"]
gamma_item = next(item for item in items if item["id"] == str(self.third_project.id))
self.assertFalse(gamma_item["has_access"])
alpha_item = next(item for item in items if item["id"] == str(self.first_project.id))
self.assertEqual(alpha_item["workspace_rate"]["hourly_rate"], "25.00")
self.assertIsNone(alpha_item["project_rate"])
grant_response = self.client.post(
"/api/projects/access/grant/",
{
"workspace": str(self.workspace.id),
"user": str(self.member.id),
"project_ids": [str(self.third_project.id)],
},
format="json",
)
self.assertEqual(grant_response.status_code, 200)
access_response = self.client.get(
"/api/projects/access/",
{"workspace": str(self.workspace.id), "user": str(self.member.id)},
)
gamma_item = next(item for item in access_response.data["items"] if item["id"] == str(self.third_project.id))
self.assertTrue(gamma_item["has_access"])
revoke_response = self.client.post(
"/api/projects/access/revoke/",
{
"workspace": str(self.workspace.id),
"user": str(self.member.id),
"project_ids": [str(self.first_project.id)],
},
format="json",
)
self.assertEqual(revoke_response.status_code, 200)
self.assertFalse(ProjectAccess.objects.filter(project=self.first_project, user=self.member).exists())
def test_project_access_rate_endpoint_saves_override_and_keeps_it_dormant_after_revoke(self):
self.client.force_authenticate(user=self.owner)
save_response = self.client.post(
"/api/projects/access/rate/",
{
"workspace": str(self.workspace.id),
"user": str(self.member.id),
"project": str(self.first_project.id),
"hourly_rate": "44.50",
"currency": "USD",
},
format="json",
)
self.assertEqual(save_response.status_code, 200)
self.assertFalse(save_response.data["removed"])
self.assertEqual(save_response.data["item"]["project_rate"]["hourly_rate"], "44.50")
self.assertTrue(
ProjectUserRate.objects.filter(project=self.first_project, user=self.member, is_deleted=False).exists()
)
revoke_response = self.client.post(
"/api/projects/access/revoke/",
{
"workspace": str(self.workspace.id),
"user": str(self.member.id),
"project_ids": [str(self.first_project.id)],
},
format="json",
)
self.assertEqual(revoke_response.status_code, 200)
self.assertTrue(
ProjectUserRate.objects.filter(project=self.first_project, user=self.member, is_deleted=False).exists()
)
access_response = self.client.get(
"/api/projects/access/",
{"workspace": str(self.workspace.id), "user": str(self.member.id)},
)
self.assertEqual(access_response.status_code, 200)
alpha_item = next(item for item in access_response.data["items"] if item["id"] == str(self.first_project.id))
self.assertFalse(alpha_item["has_access"])
self.assertEqual(alpha_item["project_rate"]["hourly_rate"], "44.50")
def test_project_access_rate_endpoint_rejects_projects_without_access(self):
self.client.force_authenticate(user=self.owner)
response = self.client.post(
"/api/projects/access/rate/",
{
"workspace": str(self.workspace.id),
"user": str(self.member.id),
"project": str(self.third_project.id),
"hourly_rate": "44.50",
"currency": "USD",
},
format="json",
)
self.assertEqual(response.status_code, 400)
self.assertIn("Grant project access", response.data["detail"])
def test_owner_access_state_marks_all_projects_as_accessible_and_allows_project_rate_override(self):
self.client.force_authenticate(user=self.owner)
access_response = self.client.get(
"/api/projects/access/",
{"workspace": str(self.workspace.id), "user": str(self.owner.id)},
)
self.assertEqual(access_response.status_code, 200)
self.assertTrue(all(item["has_access"] for item in access_response.data["items"]))
save_response = self.client.post(
"/api/projects/access/rate/",
{
"workspace": str(self.workspace.id),
"user": str(self.owner.id),
"project": str(self.first_project.id),
"hourly_rate": "60.00",
"currency": "USD",
},
format="json",
)
self.assertEqual(save_response.status_code, 200)
self.assertEqual(save_response.data["item"]["project_rate"]["hourly_rate"], "60.00")
self.assertTrue(ProjectUserRate.objects.filter(project=self.first_project, user=self.owner, is_deleted=False).exists())