238 lines
8.7 KiB
Python
238 lines
8.7 KiB
Python
from types import SimpleNamespace
|
|
|
|
from django.core.cache import cache
|
|
from django.test import TestCase
|
|
from rest_framework.test import APITestCase
|
|
|
|
from apps.projects.models import Project, ProjectAccess, ProjectUserRate
|
|
from apps.users.models import User
|
|
from apps.workspaces.api.permissions import (
|
|
CanWorkspaceManageMembers,
|
|
IsWorkspaceAdmin,
|
|
IsWorkspaceMember,
|
|
IsWorkspaceOwner,
|
|
)
|
|
from apps.workspaces.models import PriceUnit, Workspace, WorkspaceMembership, WorkspaceUserRate
|
|
|
|
|
|
class WorkspacePermissionTests(TestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.owner = User.objects.create_user(mobile="09127770021", password="secret123")
|
|
cls.admin = User.objects.create_user(mobile="09127770022", password="secret123")
|
|
cls.member = User.objects.create_user(mobile="09127770023", password="secret123")
|
|
cls.guest = User.objects.create_user(mobile="09127770024", password="secret123")
|
|
cls.outsider = User.objects.create_user(mobile="09127770025", password="secret123")
|
|
|
|
cls.workspace = Workspace.objects.create(name="Workspace Perms", owner=cls.owner)
|
|
cls.admin_membership = WorkspaceMembership.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.admin,
|
|
role=WorkspaceMembership.Role.ADMIN,
|
|
is_active=True,
|
|
)
|
|
cls.member_membership = WorkspaceMembership.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.member,
|
|
role=WorkspaceMembership.Role.MEMBER,
|
|
is_active=True,
|
|
)
|
|
cls.guest_membership = WorkspaceMembership.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.guest,
|
|
role=WorkspaceMembership.Role.GUEST,
|
|
is_active=True,
|
|
)
|
|
cls.workspace_note = SimpleNamespace(workspace=cls.workspace)
|
|
|
|
def _request_for(self, user):
|
|
return SimpleNamespace(user=user)
|
|
|
|
def test_is_workspace_owner_handles_workspace_and_membership_objects(self):
|
|
permission = IsWorkspaceOwner()
|
|
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.owner),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.owner),
|
|
None,
|
|
self.admin_membership,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
permission.has_object_permission(
|
|
self._request_for(self.admin),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
|
|
def test_is_workspace_admin_accepts_workspace_related_objects(self):
|
|
permission = IsWorkspaceAdmin()
|
|
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.admin),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.admin),
|
|
None,
|
|
self.workspace_note,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
permission.has_object_permission(
|
|
self._request_for(self.member),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
|
|
def test_is_workspace_member_allows_active_guest_but_not_outsider(self):
|
|
permission = IsWorkspaceMember()
|
|
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.guest),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
permission.has_object_permission(
|
|
self._request_for(self.outsider),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
|
|
def test_can_workspace_manage_members_only_allows_owner_and_admin(self):
|
|
permission = CanWorkspaceManageMembers()
|
|
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.owner),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
permission.has_object_permission(
|
|
self._request_for(self.admin),
|
|
None,
|
|
self.admin_membership,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
permission.has_object_permission(
|
|
self._request_for(self.member),
|
|
None,
|
|
self.workspace,
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
permission.has_object_permission(
|
|
self._request_for(self.owner),
|
|
None,
|
|
object(),
|
|
)
|
|
)
|
|
|
|
|
|
class WorkspaceMembershipCacheTests(APITestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.owner = User.objects.create_user(mobile="09127770031", password="secret123")
|
|
cls.member = User.objects.create_user(mobile="09127770032", password="secret123")
|
|
cls.workspace = Workspace.objects.create(name="Membership Cache", owner=cls.owner)
|
|
cls.membership = WorkspaceMembership.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.member,
|
|
role=WorkspaceMembership.Role.MEMBER,
|
|
is_active=True,
|
|
)
|
|
|
|
def setUp(self):
|
|
cache.clear()
|
|
self.client.force_authenticate(user=self.owner)
|
|
|
|
def test_membership_list_cache_invalidates_after_membership_save(self):
|
|
params = {"workspace": str(self.workspace.id)}
|
|
|
|
first_response = self.client.get("/api/workspace-memberships/", params)
|
|
self.assertEqual(first_response.status_code, 200)
|
|
target = next(item for item in first_response.data["items"] if item["id"] == str(self.membership.id))
|
|
self.assertEqual(target["role"], WorkspaceMembership.Role.MEMBER)
|
|
|
|
WorkspaceMembership.objects.filter(id=self.membership.id).update(role=WorkspaceMembership.Role.GUEST)
|
|
|
|
cached_response = self.client.get("/api/workspace-memberships/", params)
|
|
self.assertEqual(cached_response.status_code, 200)
|
|
target = next(item for item in cached_response.data["items"] if item["id"] == str(self.membership.id))
|
|
self.assertEqual(target["role"], WorkspaceMembership.Role.MEMBER)
|
|
|
|
self.membership.refresh_from_db()
|
|
self.membership.is_active = False
|
|
self.membership.save(update_fields=["is_active"])
|
|
|
|
fresh_response = self.client.get("/api/workspace-memberships/", params)
|
|
self.assertEqual(fresh_response.status_code, 200)
|
|
target = next(item for item in fresh_response.data["items"] if item["id"] == str(self.membership.id))
|
|
self.assertEqual(target["role"], WorkspaceMembership.Role.GUEST)
|
|
self.assertFalse(target["is_active"])
|
|
|
|
|
|
class WorkspaceMyRatesApiTests(APITestCase):
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
cls.owner = User.objects.create_user(mobile="09127770101", password="secret123")
|
|
cls.member = User.objects.create_user(mobile="09127770102", password="secret123")
|
|
cls.workspace = Workspace.objects.create(name="Rates View", owner=cls.owner)
|
|
WorkspaceMembership.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.member,
|
|
role=WorkspaceMembership.Role.MEMBER,
|
|
is_active=True,
|
|
)
|
|
PriceUnit.objects.create(code="USD", name="US Dollar", local_name="Dollar", symbol="$")
|
|
cls.project = Project.objects.create(workspace=cls.workspace, name="Mobile App")
|
|
ProjectAccess.objects.create(project=cls.project, user=cls.member)
|
|
WorkspaceUserRate.objects.create(
|
|
workspace=cls.workspace,
|
|
user=cls.member,
|
|
hourly_rate="10.00",
|
|
currency="USD",
|
|
effective_from=cls.workspace.created_at,
|
|
is_active=True,
|
|
)
|
|
ProjectUserRate.objects.create(
|
|
project=cls.project,
|
|
user=cls.member,
|
|
hourly_rate="18.00",
|
|
currency="USD",
|
|
effective_from=cls.workspace.created_at,
|
|
is_active=True,
|
|
)
|
|
|
|
def test_member_can_view_own_workspace_and_project_rates(self):
|
|
self.client.force_authenticate(user=self.member)
|
|
|
|
response = self.client.get(f"/api/workspaces/{self.workspace.id}/my-rates/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.data["workspace_rate"]["hourly_rate"], "10.00")
|
|
self.assertEqual(response.data["project_override_count"], 1)
|
|
self.assertEqual(response.data["workspace_fallback_project_count"], 0)
|
|
self.assertEqual(response.data["project_rates"][0]["project"]["name"], "Mobile App")
|
|
self.assertEqual(response.data["project_rates"][0]["rate"]["hourly_rate"], "18.00")
|