239 lines
9.6 KiB
Python
239 lines
9.6 KiB
Python
|
|
from datetime import date, time, timedelta
|
||
|
|
|
||
|
|
from django.contrib.auth import get_user_model
|
||
|
|
from django.test import TestCase
|
||
|
|
from rest_framework.test import APIRequestFactory, force_authenticate
|
||
|
|
|
||
|
|
from access_control.models import SubscriptionPlan
|
||
|
|
from farm_hub.models import FarmHub, FarmType
|
||
|
|
|
||
|
|
from .models import FarmerTodoTask, FarmerTodoZone
|
||
|
|
from .views import (
|
||
|
|
FarmerTodoDetailView,
|
||
|
|
FarmerTodoListCreateView,
|
||
|
|
FarmerTodoSummaryView,
|
||
|
|
FarmerTodoTagsView,
|
||
|
|
FarmerTodoZonesView,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class FarmerTodoViewTests(TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.factory = APIRequestFactory()
|
||
|
|
self.user = get_user_model().objects.create_user(
|
||
|
|
username="todo-user",
|
||
|
|
password="secret123",
|
||
|
|
email="todo@example.com",
|
||
|
|
phone_number="09123333333",
|
||
|
|
)
|
||
|
|
self.other_user = get_user_model().objects.create_user(
|
||
|
|
username="todo-other",
|
||
|
|
password="secret123",
|
||
|
|
email="todo-other@example.com",
|
||
|
|
phone_number="09124444444",
|
||
|
|
)
|
||
|
|
self.plan = SubscriptionPlan.objects.create(code="todo-plan", name="Todo Plan")
|
||
|
|
self.farm_type = FarmType.objects.create(name="باغی")
|
||
|
|
self.farm = FarmHub.objects.create(
|
||
|
|
owner=self.user,
|
||
|
|
farm_type=self.farm_type,
|
||
|
|
subscription_plan=self.plan,
|
||
|
|
name="Farm A",
|
||
|
|
)
|
||
|
|
self.other_farm = FarmHub.objects.create(
|
||
|
|
owner=self.other_user,
|
||
|
|
farm_type=self.farm_type,
|
||
|
|
subscription_plan=self.plan,
|
||
|
|
name="Farm B",
|
||
|
|
)
|
||
|
|
self.zone = FarmerTodoZone.objects.create(
|
||
|
|
farm=self.farm,
|
||
|
|
label="قطعه گندم - شمال مزرعه",
|
||
|
|
value="قطعه گندم - شمال مزرعه",
|
||
|
|
)
|
||
|
|
self.task = FarmerTodoTask.objects.create(
|
||
|
|
farm=self.farm,
|
||
|
|
zone=self.zone,
|
||
|
|
uuid="11111111-1111-1111-1111-111111111111",
|
||
|
|
title="بررسی رطوبت ردیف شمالی",
|
||
|
|
scheduled_date=date.today(),
|
||
|
|
time=time(6, 30),
|
||
|
|
priority=FarmerTodoTask.PRIORITY_HIGH,
|
||
|
|
description="اگر رطوبت کمتر از 28٪ بود، آبیاری دوباره بررسی شود.",
|
||
|
|
status=FarmerTodoTask.STATUS_OPEN,
|
||
|
|
extended_props={"tags": ["آبیاری"]},
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_list_tasks_returns_expected_shape(self):
|
||
|
|
request = self.factory.get(f"/api/farmer-todos/?farm_uuid={self.farm.farm_uuid}")
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoListCreateView.as_view()(request)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 200)
|
||
|
|
self.assertEqual(response.data["meta"]["total"], 1)
|
||
|
|
self.assertEqual(response.data["tasks"][0]["zone"], self.zone.value)
|
||
|
|
self.assertEqual(response.data["tasks"][0]["priority"], "زیاد")
|
||
|
|
self.assertEqual(response.data["tasks"][0]["time"], "06:30")
|
||
|
|
self.assertEqual(str(response.data["tasks"][0]["id"]), str(self.task.uuid))
|
||
|
|
|
||
|
|
def test_create_task_creates_zone_and_tags(self):
|
||
|
|
request = self.factory.post(
|
||
|
|
"/api/farmer-todos/",
|
||
|
|
{
|
||
|
|
"farm_uuid": str(self.farm.farm_uuid),
|
||
|
|
"title": "بازدید پمپ جنوب",
|
||
|
|
"zone": "انبار مرکزی",
|
||
|
|
"scheduledDate": "2025-02-24",
|
||
|
|
"time": "07:00",
|
||
|
|
"priority": "medium",
|
||
|
|
"note": "بعد از ثبت انجام، مورد غیرعادی را یادداشت کن.",
|
||
|
|
"tags": ["روزانه", "ثبت دستی"],
|
||
|
|
"status": "open",
|
||
|
|
},
|
||
|
|
format="json",
|
||
|
|
)
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoListCreateView.as_view()(request)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 201)
|
||
|
|
self.assertEqual(response.data["task"]["priority"], "متوسط")
|
||
|
|
self.assertEqual(response.data["task"]["zone"], "انبار مرکزی")
|
||
|
|
self.assertEqual(response.data["task"]["tags"], ["روزانه", "ثبت دستی"])
|
||
|
|
self.assertTrue(FarmerTodoZone.objects.filter(farm=self.farm, value="انبار مرکزی").exists())
|
||
|
|
|
||
|
|
def test_update_task_supports_status_only_payload(self):
|
||
|
|
request = self.factory.put(
|
||
|
|
f"/api/farmer-todos/{self.task.uuid}/",
|
||
|
|
{"status": "done"},
|
||
|
|
format="json",
|
||
|
|
)
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoDetailView.as_view()(request, task_id=self.task.uuid)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 200)
|
||
|
|
self.assertEqual(response.data["task"]["status"], "done")
|
||
|
|
|
||
|
|
def test_filter_by_search_and_priority(self):
|
||
|
|
FarmerTodoTask.objects.create(
|
||
|
|
farm=self.farm,
|
||
|
|
zone=self.zone,
|
||
|
|
title="نمونه برداری خاک",
|
||
|
|
scheduled_date=date(2025, 2, 25),
|
||
|
|
time=time(9, 15),
|
||
|
|
priority=FarmerTodoTask.PRIORITY_LOW,
|
||
|
|
description="سه نقطه برداشت شود.",
|
||
|
|
status=FarmerTodoTask.STATUS_OPEN,
|
||
|
|
)
|
||
|
|
request = self.factory.get(
|
||
|
|
f"/api/farmer-todos/?farm_uuid={self.farm.farm_uuid}&priority=high&search=رطوبت"
|
||
|
|
)
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoListCreateView.as_view()(request)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 200)
|
||
|
|
self.assertEqual(response.data["meta"]["total"], 1)
|
||
|
|
self.assertEqual(response.data["tasks"][0]["title"], "بررسی رطوبت ردیف شمالی")
|
||
|
|
|
||
|
|
def test_zones_and_tags_endpoints_return_separate_lists(self):
|
||
|
|
zone_request = self.factory.get(f"/api/farmer-todos/zones/?farm_uuid={self.farm.farm_uuid}")
|
||
|
|
tag_request = self.factory.get(f"/api/farmer-todos/tags/?farm_uuid={self.farm.farm_uuid}")
|
||
|
|
force_authenticate(zone_request, user=self.user)
|
||
|
|
force_authenticate(tag_request, user=self.user)
|
||
|
|
|
||
|
|
zone_response = FarmerTodoZonesView.as_view()(zone_request)
|
||
|
|
tag_response = FarmerTodoTagsView.as_view()(tag_request)
|
||
|
|
|
||
|
|
self.assertEqual(zone_response.status_code, 200)
|
||
|
|
self.assertEqual(tag_response.status_code, 200)
|
||
|
|
self.assertEqual(zone_response.data["zones"][0]["value"], self.zone.value)
|
||
|
|
self.assertTrue(any(item["value"] == "آبیاری" for item in tag_response.data["tags"]))
|
||
|
|
|
||
|
|
def test_summary_returns_expected_counts(self):
|
||
|
|
FarmerTodoTask.objects.create(
|
||
|
|
farm=self.farm,
|
||
|
|
zone=self.zone,
|
||
|
|
title="کار انجام شده",
|
||
|
|
scheduled_date=date.today(),
|
||
|
|
time=time(8, 0),
|
||
|
|
priority=FarmerTodoTask.PRIORITY_MEDIUM,
|
||
|
|
description="",
|
||
|
|
status=FarmerTodoTask.STATUS_DONE,
|
||
|
|
)
|
||
|
|
upcoming = FarmerTodoTask.objects.create(
|
||
|
|
farm=self.farm,
|
||
|
|
zone=self.zone,
|
||
|
|
title="کار بعدی",
|
||
|
|
scheduled_date=date.today() + timedelta(days=1),
|
||
|
|
time=time(7, 0),
|
||
|
|
priority=FarmerTodoTask.PRIORITY_HIGH,
|
||
|
|
description="",
|
||
|
|
status=FarmerTodoTask.STATUS_OPEN,
|
||
|
|
)
|
||
|
|
request = self.factory.get(f"/api/farmer-todos/summary/?farm_uuid={self.farm.farm_uuid}")
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoSummaryView.as_view()(request)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 200)
|
||
|
|
self.assertEqual(response.data["completedCount"], 1)
|
||
|
|
self.assertEqual(response.data["urgentCount"], 2)
|
||
|
|
self.assertEqual(str(response.data["nextTask"]["id"]), str(self.task.uuid))
|
||
|
|
self.assertNotEqual(str(response.data["nextTask"]["id"]), str(upcoming.uuid))
|
||
|
|
|
||
|
|
def test_delete_task_returns_success(self):
|
||
|
|
request = self.factory.delete(f"/api/farmer-todos/{self.task.uuid}/")
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoDetailView.as_view()(request, task_id=self.task.uuid)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 200)
|
||
|
|
self.assertEqual(response.data, {"success": True})
|
||
|
|
self.assertFalse(FarmerTodoTask.objects.filter(pk=self.task.pk).exists())
|
||
|
|
|
||
|
|
def test_validation_error_returns_message_and_details(self):
|
||
|
|
request = self.factory.post(
|
||
|
|
"/api/farmer-todos/",
|
||
|
|
{
|
||
|
|
"farm_uuid": str(self.farm.farm_uuid),
|
||
|
|
"title": "",
|
||
|
|
"priority": "unknown",
|
||
|
|
},
|
||
|
|
format="json",
|
||
|
|
)
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoListCreateView.as_view()(request)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 400)
|
||
|
|
self.assertEqual(response.data["code"], "TASK_VALIDATION_ERROR")
|
||
|
|
self.assertIn("message", response.data)
|
||
|
|
self.assertIn("details", response.data)
|
||
|
|
|
||
|
|
def test_detail_rejects_foreign_task(self):
|
||
|
|
foreign_zone = FarmerTodoZone.objects.create(
|
||
|
|
farm=self.other_farm,
|
||
|
|
label="foreign zone",
|
||
|
|
value="foreign zone",
|
||
|
|
)
|
||
|
|
foreign_task = FarmerTodoTask.objects.create(
|
||
|
|
farm=self.other_farm,
|
||
|
|
zone=foreign_zone,
|
||
|
|
uuid="22222222-2222-2222-2222-222222222222",
|
||
|
|
title="foreign task",
|
||
|
|
scheduled_date=date(2025, 2, 24),
|
||
|
|
time=time(6, 30),
|
||
|
|
priority=FarmerTodoTask.PRIORITY_HIGH,
|
||
|
|
status=FarmerTodoTask.STATUS_OPEN,
|
||
|
|
)
|
||
|
|
request = self.factory.get(f"/api/farmer-todos/{foreign_task.uuid}/")
|
||
|
|
force_authenticate(request, user=self.user)
|
||
|
|
|
||
|
|
response = FarmerTodoDetailView.as_view()(request, task_id=foreign_task.uuid)
|
||
|
|
|
||
|
|
self.assertEqual(response.status_code, 404)
|
||
|
|
self.assertEqual(response.data["message"], "Task not found.")
|