2025-03-24 09:43:19 -10:00

453 lines
18 KiB
Python

import base64
from collections import OrderedDict
from datetime import datetime
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from freezegun import freeze_time
from helpdesk.models import CustomField, Queue, Ticket
from rest_framework import HTTP_HEADER_ENCODING
from rest_framework.exceptions import ErrorDetail
from rest_framework.status import (
HTTP_200_OK,
HTTP_201_CREATED,
HTTP_204_NO_CONTENT,
HTTP_400_BAD_REQUEST,
HTTP_403_FORBIDDEN,
)
from rest_framework.test import APITestCase
class TicketTest(APITestCase):
due_date = datetime(2022, 4, 10, 15, 6)
@classmethod
def setUpTestData(cls):
cls.queue = Queue.objects.create(
title="Test Queue",
slug="test-queue",
)
def test_create_api_ticket_not_authenticated_user(self):
response = self.client.post("/api/tickets/")
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
def test_create_api_ticket_authenticated_non_staff_user(self):
non_staff_user = User.objects.create_user(username="test")
self.client.force_authenticate(non_staff_user)
response = self.client.post("/api/tickets/")
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
def test_create_api_ticket_no_data(self):
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
response = self.client.post("/api/tickets/")
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{
"queue": [
ErrorDetail(string="This field is required.", code="required")
],
"title": [
ErrorDetail(string="This field is required.", code="required")
],
},
)
self.assertFalse(Ticket.objects.exists())
def test_create_api_ticket_wrong_date_format(self):
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Test title",
"due_date": "monday, 1st of may 2022",
},
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{
"due_date": [
ErrorDetail(
string="Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].",
code="invalid",
)
]
},
)
self.assertFalse(Ticket.objects.exists())
def test_create_api_ticket_authenticated_staff_user(self):
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Test title",
"description": "Test description\nMulti lines",
"submitter_email": "test@mail.com",
"priority": 4,
},
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
created_ticket = Ticket.objects.get()
self.assertEqual(created_ticket.title, "Test title")
self.assertEqual(created_ticket.description, "Test description\nMulti lines")
self.assertEqual(created_ticket.submitter_email, "test@mail.com")
self.assertEqual(created_ticket.priority, 4)
self.assertEqual(created_ticket.followup_set.count(), 1)
def test_create_api_ticket_with_basic_auth(self):
username = "admin"
password = "admin"
User.objects.create_user(username=username, password=password, is_staff=True)
test_user = User.objects.create_user(username="test")
merge_ticket = Ticket.objects.create(queue=self.queue, title="merge ticket")
# Generate base64 credentials string
credentials = f"{username}:{password}"
base64_credentials = base64.b64encode(
credentials.encode(HTTP_HEADER_ENCODING)
).decode(HTTP_HEADER_ENCODING)
self.client.credentials(HTTP_AUTHORIZATION=f"Basic {base64_credentials}")
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Title",
"description": "Description",
"resolution": "Resolution",
"assigned_to": test_user.id,
"submitter_email": "test@mail.com",
"status": Ticket.RESOLVED_STATUS,
"priority": 1,
"on_hold": True,
"due_date": self.due_date,
"merged_to": merge_ticket.id,
},
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
created_ticket = Ticket.objects.last()
self.assertEqual(created_ticket.title, "Title")
self.assertEqual(created_ticket.description, "Description")
# resolution can not be set on creation
self.assertIsNone(created_ticket.resolution)
self.assertEqual(created_ticket.assigned_to, test_user)
self.assertEqual(created_ticket.submitter_email, "test@mail.com")
self.assertEqual(created_ticket.priority, 1)
# on_hold is False on creation
self.assertFalse(created_ticket.on_hold)
# status is always open on creation
self.assertEqual(created_ticket.status, Ticket.OPEN_STATUS)
self.assertEqual(created_ticket.due_date, self.due_date)
# merged_to can not be set on creation
self.assertIsNone(created_ticket.merged_to)
def test_edit_api_ticket(self):
staff_user = User.objects.create_user(username="admin", is_staff=True)
test_ticket = Ticket.objects.create(queue=self.queue, title="Test ticket")
test_user = User.objects.create_user(username="test")
merge_ticket = Ticket.objects.create(queue=self.queue, title="merge ticket")
self.client.force_authenticate(staff_user)
response = self.client.put(
"/api/tickets/%d/" % test_ticket.id,
{
"queue": self.queue.id,
"title": "Title",
"description": "Description",
"resolution": "Resolution",
"assigned_to": test_user.id,
"submitter_email": "test@mail.com",
"status": Ticket.RESOLVED_STATUS,
"priority": 1,
"on_hold": True,
"due_date": self.due_date,
"merged_to": merge_ticket.id,
},
)
self.assertEqual(response.status_code, HTTP_200_OK)
test_ticket.refresh_from_db()
self.assertEqual(test_ticket.title, "Title")
self.assertEqual(test_ticket.description, "Description")
self.assertEqual(test_ticket.resolution, "Resolution")
self.assertEqual(test_ticket.assigned_to, test_user)
self.assertEqual(test_ticket.submitter_email, "test@mail.com")
self.assertEqual(test_ticket.priority, 1)
self.assertTrue(test_ticket.on_hold)
self.assertEqual(test_ticket.status, Ticket.RESOLVED_STATUS)
self.assertEqual(test_ticket.due_date, self.due_date)
self.assertEqual(test_ticket.merged_to, merge_ticket)
def test_partial_edit_api_ticket(self):
staff_user = User.objects.create_user(username="admin", is_staff=True)
test_ticket = Ticket.objects.create(queue=self.queue, title="Test ticket")
self.client.force_authenticate(staff_user)
response = self.client.patch(
"/api/tickets/%d/" % test_ticket.id,
{
"description": "New description",
},
)
self.assertEqual(response.status_code, HTTP_200_OK)
test_ticket.refresh_from_db()
self.assertEqual(test_ticket.description, "New description")
def test_delete_api_ticket(self):
staff_user = User.objects.create_user(username="admin", is_staff=True)
test_ticket = Ticket.objects.create(queue=self.queue, title="Test ticket")
self.client.force_authenticate(staff_user)
response = self.client.delete("/api/tickets/%d/" % test_ticket.id)
self.assertEqual(response.status_code, HTTP_204_NO_CONTENT)
self.assertFalse(Ticket.objects.exists())
@freeze_time("2022-06-30 23:09:44")
def test_create_api_ticket_with_custom_fields(self):
# Create custom fields
for field_type, field_display in CustomField.DATA_TYPE_CHOICES:
extra_data = {}
if field_type in ("varchar", "text"):
extra_data["max_length"] = 10
if field_type == "integer":
# Set one field as required to test error if not provided
extra_data["required"] = True
if field_type == "decimal":
extra_data["max_length"] = 7
extra_data["decimal_places"] = 3
if field_type == "list":
extra_data["list_values"] = """Green
Blue
Red
Yellow"""
CustomField.objects.create(
name=field_type, label=field_display, data_type=field_type, **extra_data
)
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
# Test creation without providing required field
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Test title",
"description": "Test description\nMulti lines",
"submitter_email": "test@mail.com",
"priority": 4,
},
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{
"custom_integer": [
ErrorDetail(string="This field is required.", code="required")
]
},
)
# Test creation with custom field values
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Test title",
"description": "Test description\nMulti lines",
"submitter_email": "test@mail.com",
"priority": 4,
"custom_varchar": "test",
"custom_text": "multi\nline",
"custom_integer": "1",
"custom_decimal": "42.987",
"custom_list": "Red",
"custom_boolean": True,
"custom_date": "2022-4-11",
"custom_time": "23:59:59",
"custom_datetime": "2022-4-10 18:27",
"custom_email": "email@test.com",
"custom_url": "http://django-helpdesk.readthedocs.org/",
"custom_ipaddress": "127.0.0.1",
"custom_slug": "test-slug",
},
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
# Check all fields with data returned from the response
self.assertEqual(
response.data,
{
"id": 1,
"queue": 1,
"title": "Test title",
"description": "Test description\nMulti lines",
"resolution": None,
"submitter_email": "test@mail.com",
"assigned_to": None,
"status": 1,
"on_hold": False,
"priority": 4,
"due_date": None,
"merged_to": None,
"followup_set": [
OrderedDict(
[
("id", 1),
("ticket", 1),
("user", 1),
("title", "Ticket Opened"),
("comment", "Test description\nMulti lines"),
("public", True),
("new_status", None),
("time_spent", None),
("followupattachment_set", []),
("date", "2022-06-30T23:09:44"),
("message_id", None),
]
)
],
"custom_varchar": "test",
"custom_text": "multi\nline",
"custom_integer": 1,
"custom_decimal": "42.987",
"custom_list": "Red",
"custom_boolean": True,
"custom_date": "2022-04-11",
"custom_time": "23:59:59",
"custom_datetime": "2022-04-10T18:27",
"custom_email": "email@test.com",
"custom_url": "http://django-helpdesk.readthedocs.org/",
"custom_ipaddress": "127.0.0.1",
"custom_slug": "test-slug",
},
)
def test_create_api_ticket_with_attachment(self):
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
test_file = SimpleUploadedFile(
"file.jpg", b"file_content", content_type="image/jpg"
)
response = self.client.post(
"/api/tickets/",
{
"queue": self.queue.id,
"title": "Test title",
"description": "Test description\nMulti lines",
"submitter_email": "test@mail.com",
"priority": 4,
"attachment": test_file,
},
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
created_ticket = Ticket.objects.get()
self.assertEqual(created_ticket.title, "Test title")
self.assertEqual(created_ticket.description, "Test description\nMulti lines")
self.assertEqual(created_ticket.submitter_email, "test@mail.com")
self.assertEqual(created_ticket.priority, 4)
self.assertEqual(created_ticket.followup_set.count(), 1)
self.assertEqual(
created_ticket.followup_set.get().followupattachment_set.count(), 1
)
attachment = created_ticket.followup_set.get().followupattachment_set.get()
self.assertEqual(
attachment.file.name,
f"helpdesk/attachments/test-queue-1-{created_ticket.secret_key}/1/file.jpg",
)
def test_create_follow_up_with_attachments(self):
staff_user = User.objects.create_user(username="test", is_staff=True)
self.client.force_authenticate(staff_user)
ticket = Ticket.objects.create(queue=self.queue, title="Test")
test_file_1 = SimpleUploadedFile(
"file.jpg", b"file_content", content_type="image/jpg"
)
test_file_2 = SimpleUploadedFile(
"doc.pdf", b"Doc content", content_type="application/pdf"
)
response = self.client.post(
"/api/followups/",
{
"ticket": ticket.id,
"title": "Test",
"comment": "Test answer\nMulti lines",
"attachments": [test_file_1, test_file_2],
},
)
self.assertEqual(response.status_code, HTTP_201_CREATED)
created_followup = ticket.followup_set.last()
self.assertEqual(created_followup.title, "Test")
self.assertEqual(created_followup.comment, "Test answer\nMulti lines")
self.assertEqual(created_followup.followupattachment_set.count(), 2)
self.assertEqual(
created_followup.followupattachment_set.first().filename, "doc.pdf"
)
self.assertEqual(
created_followup.followupattachment_set.first().mime_type, "application/pdf"
)
self.assertEqual(
created_followup.followupattachment_set.last().filename, "file.jpg"
)
self.assertEqual(
created_followup.followupattachment_set.last().mime_type, "image/jpg"
)
class UserTicketTest(APITestCase):
def setUp(self):
self.queue = Queue.objects.create(title="Test queue")
self.user = User.objects.create_user(username="test")
self.client.force_authenticate(self.user)
def test_get_user_tickets(self):
user = User.objects.create_user(username="test2", email="foo@example.com")
ticket_1 = Ticket.objects.create(
queue=self.queue, title="Test 1", submitter_email="foo@example.com"
)
Ticket.objects.create(
queue=self.queue, title="Test 2", submitter_email="bar@example.com"
)
ticket_3 = Ticket.objects.create(
queue=self.queue, title="Test 3", submitter_email="foo@example.com"
)
self.client.force_authenticate(user)
response = self.client.get("/api/user_tickets/")
self.assertEqual(response.status_code, HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 2)
self.assertEqual(response.data["results"][0]["id"], ticket_3.id)
self.assertEqual(response.data["results"][1]["id"], ticket_1.id)
def test_staff_user(self):
staff_user = User.objects.create_user(
username="test2", is_staff=True, email="staff@example.com"
)
Ticket.objects.create(
queue=self.queue, title="Test 1", submitter_email="staff@example.com"
)
Ticket.objects.create(
queue=self.queue, title="Test 2", submitter_email="foo@example.com"
)
self.client.force_authenticate(staff_user)
response = self.client.get("/api/user_tickets/")
self.assertEqual(response.status_code, HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
def test_not_logged_in_user(self):
Ticket.objects.create(
queue=self.queue, title="Test 1", submitter_email="ex@example.com"
)
self.client.logout()
response = self.client.get("/api/user_tickets/")
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)