test(backend): convert existing app suites to unittest
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.clients.models import Client
|
||||
from apps.projects.models import Project
|
||||
from apps.tags.models import Tag
|
||||
@@ -12,78 +14,94 @@ from apps.workspaces.models import Workspace
|
||||
def make_aware(year, month, day, hour=9, minute=0, second=0):
|
||||
from django.utils import timezone
|
||||
|
||||
return timezone.make_aware(datetime(year, month, day, hour, minute, second), timezone.get_current_timezone())
|
||||
|
||||
|
||||
def test_time_entry_filter_supports_project_client_tags_and_custom_dates(db):
|
||||
user = User.objects.create_user(mobile="09124444444", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
client_a = Client.objects.create(workspace=workspace, name="Client A")
|
||||
client_b = Client.objects.create(workspace=workspace, name="Client B")
|
||||
project_a = Project.objects.create(workspace=workspace, client=client_a, name="Project A")
|
||||
project_b = Project.objects.create(workspace=workspace, client=client_b, name="Project B")
|
||||
tag_backend = Tag.objects.create(workspace=workspace, name="Backend", color="#0EA5E9")
|
||||
tag_ops = Tag.objects.create(workspace=workspace, name="Ops", color="#10B981")
|
||||
|
||||
entry_a = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=project_a,
|
||||
description="Backend work",
|
||||
start_time=make_aware(2026, 4, 10, 10, 0, 0),
|
||||
end_time=make_aware(2026, 4, 10, 12, 0, 0),
|
||||
)
|
||||
entry_a.tags.set([tag_backend])
|
||||
|
||||
entry_b = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=project_b,
|
||||
description="Ops work",
|
||||
start_time=make_aware(2026, 4, 18, 14, 0, 0),
|
||||
end_time=make_aware(2026, 4, 18, 15, 30, 0),
|
||||
)
|
||||
entry_b.tags.set([tag_ops])
|
||||
|
||||
queryset = TimeEntry.objects.filter(workspace=workspace, is_deleted=False)
|
||||
|
||||
filtered = TimeEntryFilter(
|
||||
data={
|
||||
"workspace": str(workspace.id),
|
||||
"project": str(project_a.id),
|
||||
"client": str(client_a.id),
|
||||
"tags": str(tag_backend.id),
|
||||
"started_after": "2026-04-01",
|
||||
"started_before": "2026-04-15",
|
||||
},
|
||||
queryset=queryset,
|
||||
).qs
|
||||
|
||||
assert list(filtered) == [entry_a]
|
||||
|
||||
|
||||
def test_time_entry_filter_supports_status_values(db):
|
||||
user = User.objects.create_user(mobile="09125555555", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
|
||||
ended_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Ended entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 0, 0),
|
||||
)
|
||||
running_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Running entry",
|
||||
start_time=make_aware(2026, 4, 15, 9, 0, 0),
|
||||
current_timezone = timezone.get_current_timezone()
|
||||
return timezone.make_aware(
|
||||
datetime(year, month, day, hour, minute, second),
|
||||
current_timezone,
|
||||
)
|
||||
|
||||
queryset = TimeEntry.objects.filter(workspace=workspace, is_deleted=False)
|
||||
|
||||
ended = TimeEntryFilter(data={"status": "ended"}, queryset=queryset).qs
|
||||
running = TimeEntryFilter(data={"status": "running"}, queryset=queryset).qs
|
||||
class TimeEntryFilterTests(TestCase):
|
||||
def test_time_entry_filter_supports_project_client_tags_and_custom_dates(self):
|
||||
user = User.objects.create_user(mobile="09124444444", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
client_a = Client.objects.create(workspace=workspace, name="Client A")
|
||||
client_b = Client.objects.create(workspace=workspace, name="Client B")
|
||||
project_a = Project.objects.create(
|
||||
workspace=workspace,
|
||||
client=client_a,
|
||||
name="Project A",
|
||||
)
|
||||
project_b = Project.objects.create(
|
||||
workspace=workspace,
|
||||
client=client_b,
|
||||
name="Project B",
|
||||
)
|
||||
tag_backend = Tag.objects.create(
|
||||
workspace=workspace,
|
||||
name="Backend",
|
||||
color="#0EA5E9",
|
||||
)
|
||||
tag_ops = Tag.objects.create(workspace=workspace, name="Ops", color="#10B981")
|
||||
|
||||
assert list(ended) == [ended_entry]
|
||||
assert list(running) == [running_entry]
|
||||
entry_a = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=project_a,
|
||||
description="Backend work",
|
||||
start_time=make_aware(2026, 4, 10, 10, 0, 0),
|
||||
end_time=make_aware(2026, 4, 10, 12, 0, 0),
|
||||
)
|
||||
entry_a.tags.set([tag_backend])
|
||||
|
||||
entry_b = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=project_b,
|
||||
description="Ops work",
|
||||
start_time=make_aware(2026, 4, 18, 14, 0, 0),
|
||||
end_time=make_aware(2026, 4, 18, 15, 30, 0),
|
||||
)
|
||||
entry_b.tags.set([tag_ops])
|
||||
|
||||
queryset = TimeEntry.objects.filter(workspace=workspace, is_deleted=False)
|
||||
|
||||
filtered = TimeEntryFilter(
|
||||
data={
|
||||
"workspace": str(workspace.id),
|
||||
"project": str(project_a.id),
|
||||
"client": str(client_a.id),
|
||||
"tags": str(tag_backend.id),
|
||||
"started_after": "2026-04-01",
|
||||
"started_before": "2026-04-15",
|
||||
},
|
||||
queryset=queryset,
|
||||
).qs
|
||||
|
||||
self.assertEqual(list(filtered), [entry_a])
|
||||
|
||||
def test_time_entry_filter_supports_status_values(self):
|
||||
user = User.objects.create_user(mobile="09125555555", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
|
||||
ended_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Ended entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 0, 0),
|
||||
)
|
||||
running_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Running entry",
|
||||
start_time=make_aware(2026, 4, 15, 9, 0, 0),
|
||||
)
|
||||
|
||||
queryset = TimeEntry.objects.filter(workspace=workspace, is_deleted=False)
|
||||
|
||||
ended = TimeEntryFilter(data={"status": "ended"}, queryset=queryset).qs
|
||||
running = TimeEntryFilter(data={"status": "running"}, queryset=queryset).qs
|
||||
|
||||
self.assertEqual(list(ended), [ended_entry])
|
||||
self.assertEqual(list(running), [running_entry])
|
||||
|
||||
@@ -1,59 +1,66 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.time_entries.api.serializers import TimeEntrySerializer
|
||||
from apps.time_entries.models import TimeEntry
|
||||
from apps.projects.models import Project
|
||||
from apps.tags.models import Tag
|
||||
from apps.time_entries.api.serializers import TimeEntrySerializer
|
||||
from apps.time_entries.models import TimeEntry
|
||||
from apps.users.models import User
|
||||
from apps.workspaces.models import Workspace
|
||||
|
||||
|
||||
def test_time_entry_serializer_keeps_seconds(db):
|
||||
user = User.objects.create_user(mobile="09123333333", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
current_timezone = timezone.get_current_timezone()
|
||||
class TimeEntrySerializerTests(TestCase):
|
||||
def test_time_entry_serializer_keeps_seconds(self):
|
||||
user = User.objects.create_user(mobile="09123333333", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
current_timezone = timezone.get_current_timezone()
|
||||
|
||||
start_time = timezone.make_aware(datetime(2026, 4, 23, 10, 15, 42), current_timezone)
|
||||
end_time = timezone.make_aware(datetime(2026, 4, 23, 11, 0, 5), current_timezone)
|
||||
start_time = timezone.make_aware(
|
||||
datetime(2026, 4, 23, 10, 15, 42),
|
||||
current_timezone,
|
||||
)
|
||||
end_time = timezone.make_aware(
|
||||
datetime(2026, 4, 23, 11, 0, 5),
|
||||
current_timezone,
|
||||
)
|
||||
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
)
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
)
|
||||
|
||||
data = TimeEntrySerializer(entry).data
|
||||
data = TimeEntrySerializer(entry).data
|
||||
|
||||
assert data["start_time"] == start_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
assert data["end_time"] == end_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.assertEqual(data["start_time"], start_time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
self.assertEqual(data["end_time"], end_time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
def test_time_entry_serializer_includes_deleted_project_and_tags(self):
|
||||
user = User.objects.create_user(mobile="09124444444", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
project = Project.objects.create(workspace=workspace, name="Legacy Project")
|
||||
tag = Tag.objects.create(workspace=workspace, name="Legacy Tag", color="#334155")
|
||||
project.delete()
|
||||
tag.delete()
|
||||
|
||||
def test_time_entry_serializer_includes_deleted_project_and_tags(db):
|
||||
user = User.objects.create_user(mobile="09124444444", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
project = Project.objects.create(workspace=workspace, name="Legacy Project")
|
||||
tag = Tag.objects.create(workspace=workspace, name="Legacy Tag", color="#334155")
|
||||
project.delete()
|
||||
tag.delete()
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=Project.all_objects.get(id=project.id),
|
||||
description="Historical work",
|
||||
start_time=timezone.now(),
|
||||
end_time=timezone.now(),
|
||||
)
|
||||
entry.tags.set([Tag.all_objects.get(id=tag.id)])
|
||||
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
project=Project.all_objects.get(id=project.id),
|
||||
description="Historical work",
|
||||
start_time=timezone.now(),
|
||||
end_time=timezone.now(),
|
||||
)
|
||||
entry.tags.set([Tag.all_objects.get(id=tag.id)])
|
||||
data = TimeEntrySerializer(entry).data
|
||||
|
||||
data = TimeEntrySerializer(entry).data
|
||||
|
||||
assert data["project"] == str(project.id)
|
||||
assert data["project_details"]["name"] == "Legacy Project"
|
||||
assert data["project_details"]["is_deleted"] is True
|
||||
assert data["tags"] == [str(tag.id)]
|
||||
assert data["tag_details"][0]["name"] == "Legacy Tag"
|
||||
assert data["tag_details"][0]["is_deleted"] is True
|
||||
self.assertEqual(data["project"], str(project.id))
|
||||
self.assertEqual(data["project_details"]["name"], "Legacy Project")
|
||||
self.assertTrue(data["project_details"]["is_deleted"])
|
||||
self.assertEqual(data["tags"], [str(tag.id)])
|
||||
self.assertEqual(data["tag_details"][0]["name"], "Legacy Tag")
|
||||
self.assertTrue(data["tag_details"][0]["is_deleted"])
|
||||
|
||||
@@ -1,78 +1,87 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from apps.projects.models import Project
|
||||
from apps.tags.models import Tag
|
||||
from apps.time_entries.services.time_entries import create_time_entry, stop_time_entry, update_time_entry
|
||||
from apps.time_entries.services.time_entries import (
|
||||
create_time_entry,
|
||||
stop_time_entry,
|
||||
update_time_entry,
|
||||
)
|
||||
from apps.users.models import User
|
||||
from apps.workspaces.models import Workspace
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def workspace_owner(db):
|
||||
user = User.objects.create_user(mobile="09121111111", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
return user, workspace
|
||||
class TimeEntryServiceTests(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.user = User.objects.create_user(mobile="09121111111", password="secret123")
|
||||
cls.workspace = Workspace.objects.create(name="Core", owner=cls.user)
|
||||
|
||||
|
||||
def test_create_time_entry_allows_only_one_running_timer_per_workspace(workspace_owner):
|
||||
user, workspace = workspace_owner
|
||||
|
||||
create_time_entry(
|
||||
user=user,
|
||||
workspace_id=workspace.id,
|
||||
start_time=timezone.now(),
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
def test_create_time_entry_allows_only_one_running_timer_per_workspace(self):
|
||||
create_time_entry(
|
||||
user=user,
|
||||
workspace_id=workspace.id,
|
||||
start_time=timezone.now() + timedelta(minutes=5),
|
||||
user=self.user,
|
||||
workspace_id=self.workspace.id,
|
||||
start_time=timezone.now(),
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
create_time_entry(
|
||||
user=self.user,
|
||||
workspace_id=self.workspace.id,
|
||||
start_time=timezone.now() + timedelta(minutes=5),
|
||||
)
|
||||
|
||||
def test_stop_time_entry_sets_end_time_and_duration(workspace_owner):
|
||||
user, workspace = workspace_owner
|
||||
entry = create_time_entry(
|
||||
user=user,
|
||||
workspace_id=workspace.id,
|
||||
start_time=timezone.now() - timedelta(hours=1),
|
||||
)
|
||||
def test_stop_time_entry_sets_end_time_and_duration(self):
|
||||
entry = create_time_entry(
|
||||
user=self.user,
|
||||
workspace_id=self.workspace.id,
|
||||
start_time=timezone.now() - timedelta(hours=1),
|
||||
)
|
||||
|
||||
stopped_entry = stop_time_entry(entry, end_time=timezone.now())
|
||||
stopped_entry = stop_time_entry(entry, end_time=timezone.now())
|
||||
|
||||
assert stopped_entry.end_time is not None
|
||||
assert stopped_entry.duration is not None
|
||||
self.assertIsNotNone(stopped_entry.end_time)
|
||||
self.assertIsNotNone(stopped_entry.duration)
|
||||
|
||||
def test_update_time_entry_preserves_deleted_project_and_tags(self):
|
||||
project = Project.objects.create(workspace=self.workspace, name="Deleted project")
|
||||
tag = Tag.objects.create(
|
||||
workspace=self.workspace,
|
||||
name="Deleted tag",
|
||||
color="#0f172a",
|
||||
)
|
||||
entry = create_time_entry(
|
||||
user=self.user,
|
||||
workspace_id=self.workspace.id,
|
||||
start_time=timezone.now() - timedelta(hours=1),
|
||||
end_time=timezone.now(),
|
||||
project=project,
|
||||
tags=[tag],
|
||||
description="Before delete",
|
||||
)
|
||||
|
||||
def test_update_time_entry_preserves_deleted_project_and_tags(workspace_owner):
|
||||
user, workspace = workspace_owner
|
||||
project = Project.objects.create(workspace=workspace, name="Deleted project")
|
||||
tag = Tag.objects.create(workspace=workspace, name="Deleted tag", color="#0f172a")
|
||||
entry = create_time_entry(
|
||||
user=user,
|
||||
workspace_id=workspace.id,
|
||||
start_time=timezone.now() - timedelta(hours=1),
|
||||
end_time=timezone.now(),
|
||||
project=project,
|
||||
tags=[tag],
|
||||
description="Before delete",
|
||||
)
|
||||
project.delete()
|
||||
tag.delete()
|
||||
|
||||
project.delete()
|
||||
tag.delete()
|
||||
updated_entry = update_time_entry(
|
||||
entry,
|
||||
project=Project.all_objects.get(id=project.id),
|
||||
tags=[Tag.all_objects.get(id=tag.id)],
|
||||
description="After delete",
|
||||
)
|
||||
|
||||
updated_entry = update_time_entry(
|
||||
entry,
|
||||
project=Project.all_objects.get(id=project.id),
|
||||
tags=[Tag.all_objects.get(id=tag.id)],
|
||||
description="After delete",
|
||||
)
|
||||
|
||||
assert updated_entry.description == "After delete"
|
||||
assert updated_entry.project_id == project.id
|
||||
assert list(Tag.all_objects.filter(time_entries=updated_entry).values_list("id", flat=True)) == [tag.id]
|
||||
self.assertEqual(updated_entry.description, "After delete")
|
||||
self.assertEqual(updated_entry.project_id, project.id)
|
||||
self.assertEqual(
|
||||
list(
|
||||
Tag.all_objects.filter(time_entries=updated_entry).values_list(
|
||||
"id",
|
||||
flat=True,
|
||||
)
|
||||
),
|
||||
[tag.id],
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from apps.tags.models import Tag
|
||||
from apps.time_entries.models import TimeEntry
|
||||
@@ -10,131 +10,132 @@ from apps.workspaces.models import Workspace
|
||||
|
||||
|
||||
def make_aware(year, month, day, hour=9, minute=0, second=0):
|
||||
return timezone.make_aware(datetime(year, month, day, hour, minute, second), timezone.get_current_timezone())
|
||||
|
||||
|
||||
def test_time_entry_list_returns_grouped_payload_for_ended_entries(db):
|
||||
user = User.objects.create_user(mobile="09126666666", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
|
||||
first_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Morning work",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Running work",
|
||||
start_time=make_aware(2026, 4, 24, 11, 0, 0),
|
||||
current_timezone = timezone.get_current_timezone()
|
||||
return timezone.make_aware(
|
||||
datetime(year, month, day, hour, minute, second),
|
||||
current_timezone,
|
||||
)
|
||||
|
||||
client = APIClient()
|
||||
client.force_authenticate(user=user)
|
||||
|
||||
response = client.get(
|
||||
"/api/time-entries/",
|
||||
{
|
||||
"workspace": str(workspace.id),
|
||||
"status": "ended",
|
||||
"limit": 10,
|
||||
"offset": 0,
|
||||
},
|
||||
)
|
||||
class TimeEntryViewTests(APITestCase):
|
||||
def test_time_entry_list_returns_grouped_payload_for_ended_entries(self):
|
||||
user = User.objects.create_user(mobile="09126666666", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data["current_page_items_count"] == 1
|
||||
assert response.data["has_more"] is False
|
||||
assert len(response.data["groups"]) == 1
|
||||
assert len(response.data["groups"][0]["days"]) == 1
|
||||
assert response.data["groups"][0]["days"][0]["entries"][0]["id"] == str(first_entry.id)
|
||||
first_entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Morning work",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Running work",
|
||||
start_time=make_aware(2026, 4, 24, 11, 0, 0),
|
||||
)
|
||||
|
||||
self.client.force_authenticate(user=user)
|
||||
response = self.client.get(
|
||||
"/api/time-entries/",
|
||||
{
|
||||
"workspace": str(workspace.id),
|
||||
"status": "ended",
|
||||
"limit": 10,
|
||||
"offset": 0,
|
||||
},
|
||||
)
|
||||
|
||||
def test_time_entry_update_preserves_current_deleted_tags(db):
|
||||
user = User.objects.create_user(mobile="09127777777", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
tag = Tag.objects.create(workspace=workspace, name="Legacy Tag", color="#475569")
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Old",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
entry.tags.set([tag])
|
||||
tag.delete()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["current_page_items_count"], 1)
|
||||
self.assertFalse(response.data["has_more"])
|
||||
self.assertEqual(len(response.data["groups"]), 1)
|
||||
self.assertEqual(len(response.data["groups"][0]["days"]), 1)
|
||||
self.assertEqual(
|
||||
response.data["groups"][0]["days"][0]["entries"][0]["id"],
|
||||
str(first_entry.id),
|
||||
)
|
||||
|
||||
client = APIClient()
|
||||
client.force_authenticate(user=user)
|
||||
def test_time_entry_update_preserves_current_deleted_tags(self):
|
||||
user = User.objects.create_user(mobile="09127777777", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
tag = Tag.objects.create(workspace=workspace, name="Legacy Tag", color="#475569")
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Old",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
entry.tags.set([tag])
|
||||
tag.delete()
|
||||
|
||||
response = client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{
|
||||
"description": "Still editable",
|
||||
"tags": [str(tag.id)],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.client.force_authenticate(user=user)
|
||||
response = self.client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{
|
||||
"description": "Still editable",
|
||||
"tags": [str(tag.id)],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data["description"] == "Still editable"
|
||||
assert response.data["tag_details"][0]["is_deleted"] is True
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["description"], "Still editable")
|
||||
self.assertTrue(response.data["tag_details"][0]["is_deleted"])
|
||||
|
||||
def test_time_entry_update_rejects_new_deleted_tag_attachment(self):
|
||||
user = User.objects.create_user(mobile="09128888888", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
deleted_tag = Tag.objects.create(
|
||||
workspace=workspace,
|
||||
name="Deleted tag",
|
||||
color="#475569",
|
||||
)
|
||||
deleted_tag.delete()
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
|
||||
def test_time_entry_update_rejects_new_deleted_tag_attachment(db):
|
||||
user = User.objects.create_user(mobile="09128888888", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
deleted_tag = Tag.objects.create(workspace=workspace, name="Deleted tag", color="#475569")
|
||||
deleted_tag.delete()
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
self.client.force_authenticate(user=user)
|
||||
response = self.client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{"tags": [str(deleted_tag.id)]},
|
||||
format="json",
|
||||
)
|
||||
|
||||
client = APIClient()
|
||||
client.force_authenticate(user=user)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn("unavailable", response.data["error"].lower())
|
||||
|
||||
response = client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{
|
||||
"tags": [str(deleted_tag.id)],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
def test_time_entry_update_can_remove_current_deleted_tag(self):
|
||||
user = User.objects.create_user(mobile="09129999999", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
deleted_tag = Tag.objects.create(
|
||||
workspace=workspace,
|
||||
name="Deleted tag",
|
||||
color="#475569",
|
||||
)
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
entry.tags.set([deleted_tag])
|
||||
deleted_tag.delete()
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "unavailable" in response.data["error"].lower()
|
||||
self.client.force_authenticate(user=user)
|
||||
response = self.client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{"tags": []},
|
||||
format="json",
|
||||
)
|
||||
|
||||
|
||||
def test_time_entry_update_can_remove_current_deleted_tag(db):
|
||||
user = User.objects.create_user(mobile="09129999999", password="secret123")
|
||||
workspace = Workspace.objects.create(name="Core", owner=user)
|
||||
deleted_tag = Tag.objects.create(workspace=workspace, name="Deleted tag", color="#475569")
|
||||
entry = TimeEntry.objects.create(
|
||||
workspace=workspace,
|
||||
user=user,
|
||||
description="Entry",
|
||||
start_time=make_aware(2026, 4, 24, 9, 0, 0),
|
||||
end_time=make_aware(2026, 4, 24, 10, 30, 0),
|
||||
)
|
||||
entry.tags.set([deleted_tag])
|
||||
deleted_tag.delete()
|
||||
|
||||
client = APIClient()
|
||||
client.force_authenticate(user=user)
|
||||
|
||||
response = client.patch(
|
||||
f"/api/time-entries/{entry.id}/",
|
||||
{
|
||||
"tags": [],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data["tags"] == []
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["tags"], [])
|
||||
|
||||
Reference in New Issue
Block a user