from django.contrib.auth.models import User from helpdesk.models import Queue, CustomField, TicketCustomFieldValue, Ticket from helpdesk.serializers import TicketSerializer from rest_framework.status import ( HTTP_201_CREATED ) from rest_framework.test import APITestCase import json import os import requests import logging # Set up a test weberver listeining on localhost:8123 for webhooks import http.server import threading from http import HTTPStatus class WebhookRequestHandler(http.server.BaseHTTPRequestHandler): server: "WebhookServer" def do_POST(self): content_length = int(self.headers['Content-Length']) body = self.rfile.read(content_length) self.server.requests.append({ 'path': self.path, 'headers': self.headers, 'body': body }) if self.path == '/new-ticket': self.server.handled_new_ticket_requests.append(json.loads(body.decode('utf-8'))) if self.path == '/new-ticket-1': self.server.handled_new_ticket_requests_1.append(json.loads(body.decode('utf-8'))) elif self.path == '/followup': self.server.handled_follow_up_requests.append(json.loads(body.decode('utf-8'))) elif self.path == '/followup-1': self.server.handled_follow_up_requests_1.append(json.loads(body.decode('utf-8'))) self.send_response(HTTPStatus.OK) self.end_headers() def do_GET(self): if not self.path == '/get-past-requests': self.send_response(HTTPStatus.NOT_FOUND) self.end_headers() return self.send_response(HTTPStatus.OK) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps({ 'new_ticket_requests': self.server.handled_new_ticket_requests, 'new_ticket_requests_1': self.server.handled_new_ticket_requests_1, 'follow_up_requests': self.server.handled_follow_up_requests, 'follow_up_requests_1': self.server.handled_follow_up_requests_1 }).encode('utf-8')) class WebhookServer(http.server.HTTPServer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.requests = [] self.handled_new_ticket_requests = [] self.handled_new_ticket_requests_1 = [] self.handled_follow_up_requests = [] self.handled_follow_up_requests_1 = [] def start(self): self.thread = threading.Thread(target=self.serve_forever) self.thread.daemon = True # Set as a daemon so it will be killed once the main thread is dead self.thread.start() def stop(self): self.shutdown() self.server_close() self.thread.join() class WebhookTest(APITestCase): @classmethod def setUpTestData(cls): cls.queue = Queue.objects.create( title='Test Queue', slug='test-queue', ) def setUp(self): staff_user = User.objects.create_user(username='test', is_staff=True) CustomField( name="my_custom_field", data_type="varchar", required=False, ).save() self.client.force_authenticate(staff_user) def test_test_server(self): server = WebhookServer(('localhost', 8123), WebhookRequestHandler) server.start() requests.post('http://localhost:8123/new-ticket', json={ "foo": "bar"}) handled_webhook_requests = requests.get('http://localhost:8123/get-past-requests').json() self.assertEqual(handled_webhook_requests['new_ticket_requests'][-1]["foo"], "bar") server.stop() def test_create_ticket_and_followup_via_api(self): server = WebhookServer(('localhost', 8124), WebhookRequestHandler) os.environ['HELPDESK_NEW_TICKET_WEBHOOK_URLS'] = 'http://localhost:8124/new-ticket, http://localhost:8124/new-ticket-1' os.environ["HELPDESK_FOLLOWUP_WEBHOOK_URLS"] = 'http://localhost:8124/followup , http://localhost:8124/followup-1' server.start() 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_my_custom_field': 'custom value', }) self.assertEqual(CustomField.objects.all().first().name, "my_custom_field") self.assertEqual(TicketCustomFieldValue.objects.get(ticket=response.data['id']).value, 'custom value') self.assertEqual(response.status_code, HTTP_201_CREATED) handled_webhook_requests = requests.get('http://localhost:8124/get-past-requests') handled_webhook_requests = handled_webhook_requests.json() self.assertTrue(len(handled_webhook_requests['new_ticket_requests']) == 1) self.assertTrue(len(handled_webhook_requests['new_ticket_requests_1']) == 1) self.assertEqual(len(handled_webhook_requests['follow_up_requests']), 0) self.assertEqual(handled_webhook_requests['new_ticket_requests'][-1]["ticket"]["title"], "Test title") self.assertEqual(handled_webhook_requests['new_ticket_requests_1'][-1]["ticket"]["title"], "Test title") self.assertEqual(handled_webhook_requests['new_ticket_requests'][-1]["ticket"]["description"], "Test description\nMulti lines") ticket = Ticket.objects.get(id=handled_webhook_requests["new_ticket_requests"][-1]["ticket"]["id"]) ticket.set_custom_field_values() serializer = TicketSerializer(ticket) self.assertEqual( list(sorted(serializer.fields.keys())), ['assigned_to', 'attachment', 'custom_my_custom_field', 'description', 'due_date', 'followup_set', 'id', 'merged_to', 'on_hold', 'priority', 'queue', 'resolution', 'status', 'submitter_email', 'title'] ) self.assertEqual(serializer.data, handled_webhook_requests["new_ticket_requests"][-1]["ticket"]) response = self.client.post('/api/followups/', { 'ticket': handled_webhook_requests['new_ticket_requests'][-1]["ticket"]["id"], "comment": "Test comment", }) self.assertEqual(response.status_code, HTTP_201_CREATED) handled_webhook_requests = requests.get('http://localhost:8124/get-past-requests') handled_webhook_requests = handled_webhook_requests.json() self.assertEqual(len(handled_webhook_requests['follow_up_requests']), 1) self.assertEqual(len(handled_webhook_requests['follow_up_requests_1']), 1) self.assertEqual(handled_webhook_requests['follow_up_requests'][-1]["ticket"]["followup_set"][-1]["comment"], "Test comment") self.assertEqual(handled_webhook_requests['follow_up_requests_1'][-1]["ticket"]["followup_set"][-1]["comment"], "Test comment") server.stop() def test_create_ticket_and_followup_via_email(self): from .. import email server = WebhookServer(('localhost', 8125), WebhookRequestHandler) os.environ['HELPDESK_NEW_TICKET_WEBHOOK_URLS'] = 'http://localhost:8125/new-ticket' os.environ["HELPDESK_FOLLOWUP_WEBHOOK_URLS"] = 'http://localhost:8125/followup' server.start() class MockMessage(dict): def __init__(self, **kwargs): self.__dict__.update(kwargs) def get_all(self, key, default=None): return self.__dict__.get(key, default) payload = { 'body': "hello", 'full_body': "hello", 'subject': "Test subject", 'queue': self.queue, 'sender_email': "user@example.com", 'priority': "1", 'files': [], } message = { "To": ["info@example.com"], "Cc": [], "Message-Id": "random1", "In-Reply-To": "", } email.create_object_from_email_message( message=MockMessage(**message), ticket_id=None, payload=payload, files=[], logger=logging.getLogger('helpdesk'), ) handled_webhook_requests = requests.get('http://localhost:8125/get-past-requests') handled_webhook_requests = handled_webhook_requests.json() self.assertEqual(len(handled_webhook_requests['new_ticket_requests']), 1) self.assertEqual(len(handled_webhook_requests['follow_up_requests']), 0) ticket_id = handled_webhook_requests['new_ticket_requests'][-1]["ticket"]['id'] from .. import models ticket = models.Ticket.objects.get(id=ticket_id) payload = { 'body': "hello", 'full_body': "hello", 'subject': f"[test-queue-{ticket_id}] Test subject", 'queue': self.queue, 'sender_email': "user@example.com", 'priority': "1", 'files': [], } message = { "To": ["info@example.com"], "Cc": [], "Message-Id": "random", "In-Reply-To": "123", } email.create_object_from_email_message( message=MockMessage(**message), ticket_id=ticket_id, payload=payload, files=[], logger=logging.getLogger('helpdesk'), ) handled_webhook_requests = requests.get('http://localhost:8125/get-past-requests') handled_webhook_requests = handled_webhook_requests.json() self.assertEqual(len(handled_webhook_requests['follow_up_requests']), 1) self.assertEqual(handled_webhook_requests['follow_up_requests'][-1]["ticket"]["followup_set"][-1]["comment"], "hello") self.assertEqual(handled_webhook_requests['follow_up_requests'][-1]["ticket"]["id"], ticket_id) server.stop()