From 3e6d5957aab5178dd498f17aa677244de9671304 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:50:53 -0700 Subject: [PATCH] Some testing --- src/documents/serialisers.py | 54 +++++------ src/documents/tests/test_api_workflows.py | 88 +++++++++++++++++ src/documents/tests/test_workflows.py | 113 ++++++++++++++++++++++ 3 files changed, 226 insertions(+), 29 deletions(-) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0dc6c28ee..f9335e611 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1891,37 +1891,33 @@ class WorkflowActionSerializer(serializers.ModelSerializer): ) if ( - "notification_subject" in attrs - and attrs["notification_subject"] is not None - and len(attrs["notification_subject"]) > 0 - and not ( - attrs["notification_destination_emails"] - or attrs["notification_destination_url"] - ) + "type" in attrs + and attrs["type"] == WorkflowAction.WorkflowActionType.NOTIFICATION ): - raise serializers.ValidationError( - "Notification subject requires destination emails or URL", - ) + if ( + "notification_subject" not in attrs + or attrs["notification_subject"] is None + or len(attrs["notification_subject"]) == 0 + or "notification_body" not in attrs + or attrs["notification_body"] is None + or len(attrs["notification_body"]) == 0 + ): + raise serializers.ValidationError( + "Notification subject and body required", + ) + elif ( + "notification_destination_emails" not in attrs + or attrs["notification_destination_emails"] is None + or len(attrs["notification_destination_emails"]) == 0 + ) and ( + "notification_destination_url" not in attrs + or attrs["notification_destination_url"] is None + or len(attrs["notification_destination_url"]) == 0 + ): + raise serializers.ValidationError( + "Notification destination emails or URL required", + ) - if ( - ( - ( - "notification_destination_emails" in attrs - and attrs["notification_destination_emails"] is not None - and len(attrs["notification_destination_emails"]) > 0 - ) - or ( - "notification_destination_url" in attrs - and attrs["notification_destination_url"] is not None - and len(attrs["notification_destination_url"]) > 0 - ) - ) - and not attrs["notification_subject"] - and not attrs["notification_body"] - ): - raise serializers.ValidationError( - "Notification subject and body required", - ) return attrs diff --git a/src/documents/tests/test_api_workflows.py b/src/documents/tests/test_api_workflows.py index 7f48347c0..eae36d766 100644 --- a/src/documents/tests/test_api_workflows.py +++ b/src/documents/tests/test_api_workflows.py @@ -433,3 +433,91 @@ class TestApiWorkflows(DirectoriesMixin, APITestCase): self.assertNotEqual(workflow.triggers.first().id, self.trigger.id) self.assertEqual(WorkflowAction.objects.all().count(), 1) self.assertNotEqual(workflow.actions.first().id, self.action.id) + + def test_notification_action_validation(self): + """ + GIVEN: + - API request to create a workflow with a notification action + WHEN: + - API is called + THEN: + - Correct HTTP response + """ + response = self.client.post( + self.ENDPOINT, + json.dumps( + { + "name": "Workflow 2", + "order": 1, + "triggers": [ + { + "type": WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + "sources": [DocumentSource.ApiUpload], + "filter_filename": "*", + }, + ], + "actions": [ + { + "type": WorkflowAction.WorkflowActionType.NOTIFICATION, + }, + ], + }, + ), + content_type="application/json", + ) + # Notification action requires subject and body + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + response = self.client.post( + self.ENDPOINT, + json.dumps( + { + "name": "Workflow 2", + "order": 1, + "triggers": [ + { + "type": WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + "sources": [DocumentSource.ApiUpload], + "filter_filename": "*", + }, + ], + "actions": [ + { + "type": WorkflowAction.WorkflowActionType.NOTIFICATION, + "notification_subject": "Subject", + "notification_body": "Body", + }, + ], + }, + ), + content_type="application/json", + ) + # Notification action requires destination emails or url + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + response = self.client.post( + self.ENDPOINT, + json.dumps( + { + "name": "Workflow 2", + "order": 1, + "triggers": [ + { + "type": WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, + "sources": [DocumentSource.ApiUpload], + "filter_filename": "*", + }, + ], + "actions": [ + { + "type": WorkflowAction.WorkflowActionType.NOTIFICATION, + "notification_subject": "Subject", + "notification_body": "Body", + "notification_destination_emails": "me@example.com", + }, + ], + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index 03de5e1c9..1f1ac82cd 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -1,3 +1,4 @@ +import json import shutil from datetime import timedelta from pathlib import Path @@ -6,12 +7,15 @@ from unittest import mock from django.contrib.auth.models import Group from django.contrib.auth.models import User +from django.test import override_settings from django.utils import timezone from guardian.shortcuts import assign_perm from guardian.shortcuts import get_groups_with_perms from guardian.shortcuts import get_users_with_perms from rest_framework.test import APITestCase +from documents.signals.handlers import run_workflows + if TYPE_CHECKING: from django.db.models import QuerySet @@ -2077,3 +2081,112 @@ class TestWorkflows(DirectoriesMixin, FileSystemAssertsMixin, APITestCase): self.assertEqual(doc.owner, self.user2) self.assertEqual(doc.tags.all().count(), 1) self.assertIn(self.t2, doc.tags.all()) + + @override_settings( + PAPERLESS_EMAIL_HOST="localhost", + EMAIL_ENABLED=True, + PAPERLESS_URL="http://localhost:8000", + ) + @mock.patch("httpx.post") + @mock.patch("django.core.mail.message.EmailMessage.send") + def test_workflow_notifcation_action(self, mock_email_send, mock_post): + """ + GIVEN: + - Document updated workflow with notification action + WHEN: + - Document that matches is updated + THEN: + - Notification is sent + """ + mock_post.return_value = mock.Mock( + status_code=200, + json=mock.Mock(return_value={"status": "ok"}), + ) + mock_email_send.return_value = 1 + + trigger = WorkflowTrigger.objects.create( + type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, + ) + action = WorkflowAction.objects.create( + type=WorkflowAction.WorkflowActionType.NOTIFICATION, + notification_subject="Test Notification: {doc_title}", + notification_body="Test message: {doc_url}", + notification_destination_emails="user@example.com", + notification_destination_url="http://paperless-ngx.com", + notification_destination_url_headers=json.dumps({"x-api-key": "test"}), + notification_include_document=False, + ) + w = Workflow.objects.create( + name="Workflow 1", + order=0, + ) + w.triggers.add(trigger) + w.actions.add(action) + w.save() + + doc = Document.objects.create( + title="sample test", + correspondent=self.c, + original_filename="sample.pdf", + ) + + run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) + + mock_email_send.assert_called_once() + mock_post.assert_called_once_with( + "http://paperless-ngx.com", + data={ + "title": "Test Notification: sample test", + "message": "Test message: http://localhost:8000/documents/1/", + }, + headers={"x-api-key": "test"}, + ) + + @override_settings( + PAPERLESS_EMAIL_HOST="localhost", + EMAIL_ENABLED=True, + PAPERLESS_URL="http://localhost:8000", + ) + def test_workflow_notification_action_fail(self): + """ + GIVEN: + - Document updated workflow with notification action + WHEN: + - Document that matches is updated + - An error occurs during notification + THEN: + - Error is logged + """ + trigger = WorkflowTrigger.objects.create( + type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, + ) + action = WorkflowAction.objects.create( + type=WorkflowAction.WorkflowActionType.NOTIFICATION, + notification_subject="Test Notification: {doc_title}", + notification_body="Test message: {doc_url}", + notification_destination_emails="me@example.com", + notification_destination_url="http://paperless-ngx.com", + notification_include_document=True, + ) + w = Workflow.objects.create( + name="Workflow 1", + order=0, + ) + w.triggers.add(trigger) + w.actions.add(action) + w.save() + + doc = Document.objects.create( + title="sample test", + correspondent=self.c, + original_filename="sample.pdf", + ) + + # fails because no file + with self.assertLogs("paperless.handlers", level="ERROR") as cm: + run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) + + expected_str = "Error occurred sending notification email" + self.assertIn(expected_str, cm.output[0]) + expected_str = "Error occurred sending notification to destination URL" + self.assertIn(expected_str, cm.output[1])