2933 lines
100 KiB
Python
2933 lines
100 KiB
Python
import datetime
|
|
import io
|
|
import json
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
import urllib.request
|
|
import uuid
|
|
import zipfile
|
|
import zoneinfo
|
|
from datetime import timedelta
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
from unittest.mock import MagicMock
|
|
|
|
import celery
|
|
from dateutil import parser
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import Permission
|
|
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 rest_framework import status
|
|
from rest_framework.test import APITestCase
|
|
|
|
from documents.models import Correspondent
|
|
from documents.models import CustomField
|
|
from documents.models import CustomFieldInstance
|
|
from documents.models import Document
|
|
from documents.models import DocumentType
|
|
from documents.models import MatchingModel
|
|
from documents.models import Note
|
|
from documents.models import PaperlessTask
|
|
from documents.models import SavedView
|
|
from documents.models import ShareLink
|
|
from documents.models import StoragePath
|
|
from documents.models import Tag
|
|
from documents.tests.utils import DirectoriesMixin
|
|
from documents.tests.utils import DocumentConsumeDelayMixin
|
|
from paperless import version
|
|
|
|
|
|
class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = User.objects.create_superuser(username="temp_admin")
|
|
self.client.force_authenticate(user=self.user)
|
|
|
|
def testDocuments(self):
|
|
response = self.client.get("/api/documents/").data
|
|
|
|
self.assertEqual(response["count"], 0)
|
|
|
|
c = Correspondent.objects.create(name="c", pk=41)
|
|
dt = DocumentType.objects.create(name="dt", pk=63)
|
|
tag = Tag.objects.create(name="t", pk=85)
|
|
|
|
doc = Document.objects.create(
|
|
title="WOW",
|
|
content="the content",
|
|
correspondent=c,
|
|
document_type=dt,
|
|
checksum="123",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
doc.tags.add(tag)
|
|
|
|
response = self.client.get("/api/documents/", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["count"], 1)
|
|
|
|
returned_doc = response.data["results"][0]
|
|
self.assertEqual(returned_doc["id"], doc.id)
|
|
self.assertEqual(returned_doc["title"], doc.title)
|
|
self.assertEqual(returned_doc["correspondent"], c.id)
|
|
self.assertEqual(returned_doc["document_type"], dt.id)
|
|
self.assertListEqual(returned_doc["tags"], [tag.id])
|
|
|
|
c2 = Correspondent.objects.create(name="c2")
|
|
|
|
returned_doc["correspondent"] = c2.pk
|
|
returned_doc["title"] = "the new title"
|
|
|
|
response = self.client.put(
|
|
f"/api/documents/{doc.pk}/",
|
|
returned_doc,
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
doc_after_save = Document.objects.get(id=doc.id)
|
|
|
|
self.assertEqual(doc_after_save.correspondent, c2)
|
|
self.assertEqual(doc_after_save.title, "the new title")
|
|
|
|
self.client.delete(f"/api/documents/{doc_after_save.pk}/")
|
|
|
|
self.assertEqual(len(Document.objects.all()), 0)
|
|
|
|
def test_document_fields(self):
|
|
c = Correspondent.objects.create(name="c", pk=41)
|
|
dt = DocumentType.objects.create(name="dt", pk=63)
|
|
Tag.objects.create(name="t", pk=85)
|
|
storage_path = StoragePath.objects.create(name="sp", pk=77, path="p")
|
|
Document.objects.create(
|
|
title="WOW",
|
|
content="the content",
|
|
correspondent=c,
|
|
document_type=dt,
|
|
checksum="123",
|
|
mime_type="application/pdf",
|
|
storage_path=storage_path,
|
|
)
|
|
|
|
response = self.client.get("/api/documents/", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results_full = response.data["results"]
|
|
self.assertIn("content", results_full[0])
|
|
self.assertIn("id", results_full[0])
|
|
|
|
response = self.client.get("/api/documents/?fields=id", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertFalse("content" in results[0])
|
|
self.assertIn("id", results[0])
|
|
self.assertEqual(len(results[0]), 1)
|
|
|
|
response = self.client.get("/api/documents/?fields=content", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertIn("content", results[0])
|
|
self.assertFalse("id" in results[0])
|
|
self.assertEqual(len(results[0]), 1)
|
|
|
|
response = self.client.get("/api/documents/?fields=id,content", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertIn("content", results[0])
|
|
self.assertIn("id", results[0])
|
|
self.assertEqual(len(results[0]), 2)
|
|
|
|
response = self.client.get(
|
|
"/api/documents/?fields=id,conteasdnt",
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertFalse("content" in results[0])
|
|
self.assertIn("id", results[0])
|
|
self.assertEqual(len(results[0]), 1)
|
|
|
|
response = self.client.get("/api/documents/?fields=", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results_full[0]), len(results[0]))
|
|
|
|
response = self.client.get("/api/documents/?fields=dgfhs", format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results[0]), 0)
|
|
|
|
def test_document_actions(self):
|
|
_, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
|
|
|
|
content = b"This is a test"
|
|
content_thumbnail = b"thumbnail content"
|
|
|
|
with open(filename, "wb") as f:
|
|
f.write(content)
|
|
|
|
doc = Document.objects.create(
|
|
title="none",
|
|
filename=os.path.basename(filename),
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
with open(
|
|
os.path.join(self.dirs.thumbnail_dir, f"{doc.pk:07d}.webp"),
|
|
"wb",
|
|
) as f:
|
|
f.write(content_thumbnail)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/download/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/preview/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content_thumbnail)
|
|
|
|
def test_document_actions_with_perms(self):
|
|
"""
|
|
GIVEN:
|
|
- Document with owner and without granted permissions
|
|
- User is then granted permissions
|
|
WHEN:
|
|
- User tries to load preview, thumbnail
|
|
THEN:
|
|
- Initially, HTTP 403 Forbidden
|
|
- With permissions, HTTP 200 OK
|
|
"""
|
|
_, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
|
|
|
|
content = b"This is a test"
|
|
content_thumbnail = b"thumbnail content"
|
|
|
|
with open(filename, "wb") as f:
|
|
f.write(content)
|
|
|
|
user1 = User.objects.create_user(username="test1")
|
|
user2 = User.objects.create_user(username="test2")
|
|
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
|
|
user2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
|
|
|
|
self.client.force_authenticate(user2)
|
|
|
|
doc = Document.objects.create(
|
|
title="none",
|
|
filename=os.path.basename(filename),
|
|
mime_type="application/pdf",
|
|
owner=user1,
|
|
)
|
|
|
|
with open(
|
|
os.path.join(self.dirs.thumbnail_dir, f"{doc.pk:07d}.webp"),
|
|
"wb",
|
|
) as f:
|
|
f.write(content_thumbnail)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/download/")
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/preview/")
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
assign_perm("view_document", user2, doc)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/download/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/preview/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
@override_settings(FILENAME_FORMAT="")
|
|
def test_download_with_archive(self):
|
|
content = b"This is a test"
|
|
content_archive = b"This is the same test but archived"
|
|
|
|
doc = Document.objects.create(
|
|
title="none",
|
|
filename="my_document.pdf",
|
|
archive_filename="archived.pdf",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
with open(doc.source_path, "wb") as f:
|
|
f.write(content)
|
|
|
|
with open(doc.archive_path, "wb") as f:
|
|
f.write(content_archive)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/download/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content_archive)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/{doc.pk}/download/?original=true",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/preview/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content_archive)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/{doc.pk}/preview/?original=true",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.content, content)
|
|
|
|
def test_document_actions_not_existing_file(self):
|
|
doc = Document.objects.create(
|
|
title="none",
|
|
filename=os.path.basename("asd"),
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/download/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/preview/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
def test_document_filters(self):
|
|
doc1 = Document.objects.create(
|
|
title="none1",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc2 = Document.objects.create(
|
|
title="none2",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc3 = Document.objects.create(
|
|
title="none3",
|
|
checksum="C",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
tag_inbox = Tag.objects.create(name="t1", is_inbox_tag=True)
|
|
tag_2 = Tag.objects.create(name="t2")
|
|
tag_3 = Tag.objects.create(name="t3")
|
|
|
|
cf1 = CustomField.objects.create(
|
|
name="stringfield",
|
|
data_type=CustomField.FieldDataType.STRING,
|
|
)
|
|
cf2 = CustomField.objects.create(
|
|
name="numberfield",
|
|
data_type=CustomField.FieldDataType.INT,
|
|
)
|
|
|
|
doc1.tags.add(tag_inbox)
|
|
doc2.tags.add(tag_2)
|
|
doc3.tags.add(tag_2)
|
|
doc3.tags.add(tag_3)
|
|
|
|
cf1_d1 = CustomFieldInstance.objects.create(
|
|
document=doc1,
|
|
field=cf1,
|
|
value_text="foobard1",
|
|
)
|
|
CustomFieldInstance.objects.create(
|
|
document=doc1,
|
|
field=cf2,
|
|
value_int=999,
|
|
)
|
|
cf1_d3 = CustomFieldInstance.objects.create(
|
|
document=doc3,
|
|
field=cf1,
|
|
value_text="foobard3",
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?is_in_inbox=true")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc1.id)
|
|
|
|
response = self.client.get("/api/documents/?is_in_inbox=false")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
self.assertCountEqual([results[0]["id"], results[1]["id"]], [doc2.id, doc3.id])
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__in={tag_inbox.id},{tag_3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
self.assertCountEqual([results[0]["id"], results[1]["id"]], [doc1.id, doc3.id])
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__in={tag_2.id},{tag_3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
self.assertCountEqual([results[0]["id"], results[1]["id"]], [doc2.id, doc3.id])
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__all={tag_2.id},{tag_3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc3.id)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__all={tag_inbox.id},{tag_3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 0)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__all={tag_inbox.id}a{tag_3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
|
|
response = self.client.get(f"/api/documents/?tags__id__none={tag_3.id}")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
self.assertCountEqual([results[0]["id"], results[1]["id"]], [doc1.id, doc2.id])
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__none={tag_3.id},{tag_2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc1.id)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__none={tag_2.id},{tag_inbox.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 0)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?id__in={doc1.id},{doc2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?id__range={doc1.id},{doc3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?id={doc2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
|
|
# custom field name
|
|
response = self.client.get(
|
|
f"/api/documents/?custom_fields__icontains={cf1.name}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
# custom field value
|
|
response = self.client.get(
|
|
f"/api/documents/?custom_fields__icontains={cf1_d1.value}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc1.id)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?custom_fields__icontains={cf1_d3.value}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc3.id)
|
|
|
|
def test_document_checksum_filter(self):
|
|
Document.objects.create(
|
|
title="none1",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc2 = Document.objects.create(
|
|
title="none2",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
)
|
|
Document.objects.create(
|
|
title="none3",
|
|
checksum="C",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?checksum__iexact=B")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc2.id)
|
|
|
|
response = self.client.get("/api/documents/?checksum__iexact=X")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 0)
|
|
|
|
def test_document_original_filename_filter(self):
|
|
doc1 = Document.objects.create(
|
|
title="none1",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
original_filename="docA.pdf",
|
|
)
|
|
doc2 = Document.objects.create(
|
|
title="none2",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
original_filename="docB.pdf",
|
|
)
|
|
doc3 = Document.objects.create(
|
|
title="none3",
|
|
checksum="C",
|
|
mime_type="application/pdf",
|
|
original_filename="docC.pdf",
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?original_filename__iexact=DOCa.pdf")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]["id"], doc1.id)
|
|
|
|
response = self.client.get("/api/documents/?original_filename__iexact=docx.pdf")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 0)
|
|
|
|
response = self.client.get("/api/documents/?original_filename__istartswith=dOc")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
self.assertCountEqual(
|
|
[results[0]["id"], results[1]["id"], results[2]["id"]],
|
|
[doc1.id, doc2.id, doc3.id],
|
|
)
|
|
|
|
def test_documents_title_content_filter(self):
|
|
doc1 = Document.objects.create(
|
|
title="title A",
|
|
content="content A",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc2 = Document.objects.create(
|
|
title="title B",
|
|
content="content A",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc3 = Document.objects.create(
|
|
title="title A",
|
|
content="content B",
|
|
checksum="C",
|
|
mime_type="application/pdf",
|
|
)
|
|
doc4 = Document.objects.create(
|
|
title="title B",
|
|
content="content B",
|
|
checksum="D",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?title_content=A")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
self.assertCountEqual(
|
|
[results[0]["id"], results[1]["id"], results[2]["id"]],
|
|
[doc1.id, doc2.id, doc3.id],
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?title_content=B")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
self.assertCountEqual(
|
|
[results[0]["id"], results[1]["id"], results[2]["id"]],
|
|
[doc2.id, doc3.id, doc4.id],
|
|
)
|
|
|
|
response = self.client.get("/api/documents/?title_content=X")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 0)
|
|
|
|
def test_document_owner_filters(self):
|
|
"""
|
|
GIVEN:
|
|
- Documents with owners, with and without granted permissions
|
|
WHEN:
|
|
- User filters by owner
|
|
THEN:
|
|
- Owner filters work correctly but still respect permissions
|
|
"""
|
|
u1 = User.objects.create_user("user1")
|
|
u2 = User.objects.create_user("user2")
|
|
u1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
|
|
u2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
|
|
|
|
u1_doc1 = Document.objects.create(
|
|
title="none1",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
owner=u1,
|
|
)
|
|
Document.objects.create(
|
|
title="none2",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
owner=u2,
|
|
)
|
|
u0_doc1 = Document.objects.create(
|
|
title="none3",
|
|
checksum="C",
|
|
mime_type="application/pdf",
|
|
)
|
|
u1_doc2 = Document.objects.create(
|
|
title="none4",
|
|
checksum="D",
|
|
mime_type="application/pdf",
|
|
owner=u1,
|
|
)
|
|
u2_doc2 = Document.objects.create(
|
|
title="none5",
|
|
checksum="E",
|
|
mime_type="application/pdf",
|
|
owner=u2,
|
|
)
|
|
|
|
self.client.force_authenticate(user=u1)
|
|
assign_perm("view_document", u1, u2_doc2)
|
|
|
|
# Will not show any u1 docs or u2_doc1 which isn't shared
|
|
response = self.client.get(f"/api/documents/?owner__id__none={u1.id}")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
self.assertCountEqual(
|
|
[results[0]["id"], results[1]["id"]],
|
|
[u0_doc1.id, u2_doc2.id],
|
|
)
|
|
|
|
# Will not show any u1 docs, u0_doc1 which has no owner or u2_doc1 which isn't shared
|
|
response = self.client.get(
|
|
f"/api/documents/?owner__id__none={u1.id}&owner__isnull=false",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertCountEqual([results[0]["id"]], [u2_doc2.id])
|
|
|
|
# Will not show any u1 docs, u2_doc2 which is shared but has owner
|
|
response = self.client.get(
|
|
f"/api/documents/?owner__id__none={u1.id}&owner__isnull=true",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertCountEqual([results[0]["id"]], [u0_doc1.id])
|
|
|
|
# Will not show any u1 docs or u2_doc1 which is not shared
|
|
response = self.client.get(f"/api/documents/?owner__id={u2.id}")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
self.assertCountEqual([results[0]["id"]], [u2_doc2.id])
|
|
|
|
# Will not show u2_doc1 which is not shared
|
|
response = self.client.get(f"/api/documents/?owner__id__in={u1.id},{u2.id}")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 3)
|
|
self.assertCountEqual(
|
|
[results[0]["id"], results[1]["id"], results[2]["id"]],
|
|
[u1_doc1.id, u1_doc2.id, u2_doc2.id],
|
|
)
|
|
|
|
def test_pagination_all(self):
|
|
"""
|
|
GIVEN:
|
|
- A set of 50 documents
|
|
WHEN:
|
|
- API request for document filtering
|
|
THEN:
|
|
- Results are paginated (25 items) and response["all"] returns all ids (50 items)
|
|
"""
|
|
t = Tag.objects.create(name="tag")
|
|
docs = []
|
|
for i in range(50):
|
|
d = Document.objects.create(checksum=i, content=f"test{i}")
|
|
d.tags.add(t)
|
|
docs.append(d)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/?tags__id__in={t.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 25)
|
|
self.assertEqual(len(response.data["all"]), 50)
|
|
self.assertCountEqual(response.data["all"], [d.id for d in docs])
|
|
|
|
def test_statistics(self):
|
|
doc1 = Document.objects.create(
|
|
title="none1",
|
|
checksum="A",
|
|
mime_type="application/pdf",
|
|
content="abc",
|
|
)
|
|
Document.objects.create(
|
|
title="none2",
|
|
checksum="B",
|
|
mime_type="application/pdf",
|
|
content="123",
|
|
)
|
|
Document.objects.create(
|
|
title="none3",
|
|
checksum="C",
|
|
mime_type="text/plain",
|
|
content="hello",
|
|
)
|
|
|
|
tag_inbox = Tag.objects.create(name="t1", is_inbox_tag=True)
|
|
Tag.objects.create(name="t2")
|
|
Tag.objects.create(name="t3")
|
|
Correspondent.objects.create(name="c1")
|
|
Correspondent.objects.create(name="c2")
|
|
DocumentType.objects.create(name="dt1")
|
|
StoragePath.objects.create(name="sp1")
|
|
StoragePath.objects.create(name="sp2")
|
|
|
|
doc1.tags.add(tag_inbox)
|
|
|
|
response = self.client.get("/api/statistics/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["documents_total"], 3)
|
|
self.assertEqual(response.data["documents_inbox"], 1)
|
|
self.assertEqual(response.data["inbox_tag"], tag_inbox.pk)
|
|
self.assertEqual(
|
|
response.data["document_file_type_counts"][0]["mime_type_count"],
|
|
2,
|
|
)
|
|
self.assertEqual(
|
|
response.data["document_file_type_counts"][1]["mime_type_count"],
|
|
1,
|
|
)
|
|
self.assertEqual(response.data["character_count"], 11)
|
|
self.assertEqual(response.data["tag_count"], 3)
|
|
self.assertEqual(response.data["correspondent_count"], 2)
|
|
self.assertEqual(response.data["document_type_count"], 1)
|
|
self.assertEqual(response.data["storage_path_count"], 2)
|
|
|
|
def test_statistics_no_inbox_tag(self):
|
|
Document.objects.create(title="none1", checksum="A")
|
|
|
|
response = self.client.get("/api/statistics/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["documents_inbox"], None)
|
|
self.assertEqual(response.data["inbox_tag"], None)
|
|
|
|
def test_upload(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f},
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
input_doc, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(input_doc.original_file.name, "simple.pdf")
|
|
self.assertIn(Path(settings.SCRATCH_DIR), input_doc.original_file.parents)
|
|
self.assertIsNone(overrides.title)
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
|
|
def test_upload_empty_metadata(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "title": "", "correspondent": "", "document_type": ""},
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
input_doc, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(input_doc.original_file.name, "simple.pdf")
|
|
self.assertIn(Path(settings.SCRATCH_DIR), input_doc.original_file.parents)
|
|
self.assertIsNone(overrides.title)
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
|
|
def test_upload_invalid_form(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"documenst": f},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
self.consume_file_mock.assert_not_called()
|
|
|
|
def test_upload_invalid_file(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.zip"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
self.consume_file_mock.assert_not_called()
|
|
|
|
def test_upload_with_title(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "title": "my custom title"},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
_, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(overrides.title, "my custom title")
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
|
|
def test_upload_with_correspondent(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
c = Correspondent.objects.create(name="test-corres")
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "correspondent": c.id},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
_, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(overrides.correspondent_id, c.id)
|
|
self.assertIsNone(overrides.title)
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
|
|
def test_upload_with_invalid_correspondent(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "correspondent": 3456},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
self.consume_file_mock.assert_not_called()
|
|
|
|
def test_upload_with_document_type(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
dt = DocumentType.objects.create(name="invoice")
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "document_type": dt.id},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
_, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(overrides.document_type_id, dt.id)
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.title)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
|
|
def test_upload_with_invalid_document_type(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "document_type": 34578},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
self.consume_file_mock.assert_not_called()
|
|
|
|
def test_upload_with_tags(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
t1 = Tag.objects.create(name="tag1")
|
|
t2 = Tag.objects.create(name="tag2")
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "tags": [t2.id, t1.id]},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
_, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertCountEqual(overrides.tag_ids, [t1.id, t2.id])
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.title)
|
|
|
|
def test_upload_with_invalid_tags(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
t1 = Tag.objects.create(name="tag1")
|
|
t2 = Tag.objects.create(name="tag2")
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "tags": [t2.id, t1.id, 734563]},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
self.consume_file_mock.assert_not_called()
|
|
|
|
def test_upload_with_created(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
created = datetime.datetime(
|
|
2022,
|
|
5,
|
|
12,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"),
|
|
)
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "created": created},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
_, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(overrides.created, created)
|
|
|
|
def test_upload_with_asn(self):
|
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
|
id=str(uuid.uuid4()),
|
|
)
|
|
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
"rb",
|
|
) as f:
|
|
response = self.client.post(
|
|
"/api/documents/post_document/",
|
|
{"document": f, "archive_serial_number": 500},
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.consume_file_mock.assert_called_once()
|
|
|
|
input_doc, overrides = self.get_last_consume_delay_call_args()
|
|
|
|
self.assertEqual(input_doc.original_file.name, "simple.pdf")
|
|
self.assertEqual(overrides.filename, "simple.pdf")
|
|
self.assertIsNone(overrides.correspondent_id)
|
|
self.assertIsNone(overrides.document_type_id)
|
|
self.assertIsNone(overrides.tag_ids)
|
|
self.assertEqual(500, overrides.asn)
|
|
|
|
def test_get_metadata(self):
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
filename="file.pdf",
|
|
mime_type="image/png",
|
|
archive_checksum="A",
|
|
archive_filename="archive.pdf",
|
|
)
|
|
|
|
source_file = os.path.join(
|
|
os.path.dirname(__file__),
|
|
"samples",
|
|
"documents",
|
|
"thumbnails",
|
|
"0000001.webp",
|
|
)
|
|
archive_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
|
|
|
|
shutil.copy(source_file, doc.source_path)
|
|
shutil.copy(archive_file, doc.archive_path)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
meta = response.data
|
|
|
|
self.assertEqual(meta["original_mime_type"], "image/png")
|
|
self.assertTrue(meta["has_archive_version"])
|
|
self.assertEqual(len(meta["original_metadata"]), 0)
|
|
self.assertGreater(len(meta["archive_metadata"]), 0)
|
|
self.assertEqual(meta["media_filename"], "file.pdf")
|
|
self.assertEqual(meta["archive_media_filename"], "archive.pdf")
|
|
self.assertEqual(meta["original_size"], os.stat(source_file).st_size)
|
|
self.assertEqual(meta["archive_size"], os.stat(archive_file).st_size)
|
|
|
|
def test_get_metadata_invalid_doc(self):
|
|
response = self.client.get("/api/documents/34576/metadata/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
def test_get_metadata_no_archive(self):
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
filename="file.pdf",
|
|
mime_type="application/pdf",
|
|
)
|
|
|
|
shutil.copy(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
doc.source_path,
|
|
)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
meta = response.data
|
|
|
|
self.assertEqual(meta["original_mime_type"], "application/pdf")
|
|
self.assertFalse(meta["has_archive_version"])
|
|
self.assertGreater(len(meta["original_metadata"]), 0)
|
|
self.assertIsNone(meta["archive_metadata"])
|
|
self.assertIsNone(meta["archive_media_filename"])
|
|
|
|
def test_get_metadata_missing_files(self):
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
filename="file.pdf",
|
|
mime_type="application/pdf",
|
|
archive_filename="file.pdf",
|
|
archive_checksum="B",
|
|
checksum="A",
|
|
)
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
meta = response.data
|
|
|
|
self.assertTrue(meta["has_archive_version"])
|
|
self.assertIsNone(meta["original_metadata"])
|
|
self.assertIsNone(meta["original_size"])
|
|
self.assertIsNone(meta["archive_metadata"])
|
|
self.assertIsNone(meta["archive_size"])
|
|
|
|
def test_get_empty_suggestions(self):
|
|
doc = Document.objects.create(title="test", mime_type="application/pdf")
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/suggestions/")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(
|
|
response.data,
|
|
{
|
|
"correspondents": [],
|
|
"tags": [],
|
|
"document_types": [],
|
|
"storage_paths": [],
|
|
"dates": [],
|
|
},
|
|
)
|
|
|
|
def test_get_suggestions_invalid_doc(self):
|
|
response = self.client.get("/api/documents/34676/suggestions/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
@mock.patch("documents.views.match_storage_paths")
|
|
@mock.patch("documents.views.match_document_types")
|
|
@mock.patch("documents.views.match_tags")
|
|
@mock.patch("documents.views.match_correspondents")
|
|
@override_settings(NUMBER_OF_SUGGESTED_DATES=10)
|
|
def test_get_suggestions(
|
|
self,
|
|
match_correspondents,
|
|
match_tags,
|
|
match_document_types,
|
|
match_storage_paths,
|
|
):
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is an invoice from 12.04.2022!",
|
|
)
|
|
|
|
match_correspondents.return_value = [Correspondent(id=88), Correspondent(id=2)]
|
|
match_tags.return_value = [Tag(id=56), Tag(id=123)]
|
|
match_document_types.return_value = [DocumentType(id=23)]
|
|
match_storage_paths.return_value = [StoragePath(id=99), StoragePath(id=77)]
|
|
|
|
response = self.client.get(f"/api/documents/{doc.pk}/suggestions/")
|
|
self.assertEqual(
|
|
response.data,
|
|
{
|
|
"correspondents": [88, 2],
|
|
"tags": [56, 123],
|
|
"document_types": [23],
|
|
"storage_paths": [99, 77],
|
|
"dates": ["2022-04-12"],
|
|
},
|
|
)
|
|
|
|
@mock.patch("documents.parsers.parse_date_generator")
|
|
@override_settings(NUMBER_OF_SUGGESTED_DATES=0)
|
|
def test_get_suggestions_dates_disabled(
|
|
self,
|
|
parse_date_generator,
|
|
):
|
|
"""
|
|
GIVEN:
|
|
- NUMBER_OF_SUGGESTED_DATES = 0 (disables feature)
|
|
WHEN:
|
|
- API reuqest for document suggestions
|
|
THEN:
|
|
- Dont check for suggested dates at all
|
|
"""
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is an invoice from 12.04.2022!",
|
|
)
|
|
|
|
self.client.get(f"/api/documents/{doc.pk}/suggestions/")
|
|
self.assertFalse(parse_date_generator.called)
|
|
|
|
def test_saved_views(self):
|
|
u1 = User.objects.create_superuser("user1")
|
|
u2 = User.objects.create_superuser("user2")
|
|
|
|
v1 = SavedView.objects.create(
|
|
owner=u1,
|
|
name="test1",
|
|
sort_field="",
|
|
show_on_dashboard=False,
|
|
show_in_sidebar=False,
|
|
)
|
|
SavedView.objects.create(
|
|
owner=u2,
|
|
name="test2",
|
|
sort_field="",
|
|
show_on_dashboard=False,
|
|
show_in_sidebar=False,
|
|
)
|
|
SavedView.objects.create(
|
|
owner=u2,
|
|
name="test3",
|
|
sort_field="",
|
|
show_on_dashboard=False,
|
|
show_in_sidebar=False,
|
|
)
|
|
|
|
response = self.client.get("/api/saved_views/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["count"], 0)
|
|
|
|
self.assertEqual(
|
|
self.client.get(f"/api/saved_views/{v1.id}/").status_code,
|
|
status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
self.client.force_authenticate(user=u1)
|
|
|
|
response = self.client.get("/api/saved_views/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["count"], 1)
|
|
|
|
self.assertEqual(
|
|
self.client.get(f"/api/saved_views/{v1.id}/").status_code,
|
|
status.HTTP_200_OK,
|
|
)
|
|
|
|
self.client.force_authenticate(user=u2)
|
|
|
|
response = self.client.get("/api/saved_views/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["count"], 2)
|
|
|
|
self.assertEqual(
|
|
self.client.get(f"/api/saved_views/{v1.id}/").status_code,
|
|
status.HTTP_404_NOT_FOUND,
|
|
)
|
|
|
|
def test_create_update_patch(self):
|
|
User.objects.create_user("user1")
|
|
|
|
view = {
|
|
"name": "test",
|
|
"show_on_dashboard": True,
|
|
"show_in_sidebar": True,
|
|
"sort_field": "created2",
|
|
"filter_rules": [{"rule_type": 4, "value": "test"}],
|
|
}
|
|
|
|
response = self.client.post("/api/saved_views/", view, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
|
|
v1 = SavedView.objects.get(name="test")
|
|
self.assertEqual(v1.sort_field, "created2")
|
|
self.assertEqual(v1.filter_rules.count(), 1)
|
|
self.assertEqual(v1.owner, self.user)
|
|
|
|
response = self.client.patch(
|
|
f"/api/saved_views/{v1.id}/",
|
|
{"show_in_sidebar": False},
|
|
format="json",
|
|
)
|
|
|
|
v1 = SavedView.objects.get(id=v1.id)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertFalse(v1.show_in_sidebar)
|
|
self.assertEqual(v1.filter_rules.count(), 1)
|
|
|
|
view["filter_rules"] = [{"rule_type": 12, "value": "secret"}]
|
|
|
|
response = self.client.put(f"/api/saved_views/{v1.id}/", view, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
v1 = SavedView.objects.get(id=v1.id)
|
|
self.assertEqual(v1.filter_rules.count(), 1)
|
|
self.assertEqual(v1.filter_rules.first().value, "secret")
|
|
|
|
view["filter_rules"] = []
|
|
|
|
response = self.client.put(f"/api/saved_views/{v1.id}/", view, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
v1 = SavedView.objects.get(id=v1.id)
|
|
self.assertEqual(v1.filter_rules.count(), 0)
|
|
|
|
def test_get_logs(self):
|
|
log_data = "test\ntest2\n"
|
|
with open(os.path.join(settings.LOGGING_DIR, "mail.log"), "w") as f:
|
|
f.write(log_data)
|
|
with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f:
|
|
f.write(log_data)
|
|
response = self.client.get("/api/logs/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertCountEqual(response.data, ["mail", "paperless"])
|
|
|
|
def test_get_logs_only_when_exist(self):
|
|
log_data = "test\ntest2\n"
|
|
with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f:
|
|
f.write(log_data)
|
|
response = self.client.get("/api/logs/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertCountEqual(response.data, ["paperless"])
|
|
|
|
def test_get_invalid_log(self):
|
|
response = self.client.get("/api/logs/bogus_log/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
@override_settings(LOGGING_DIR="bogus_dir")
|
|
def test_get_nonexistent_log(self):
|
|
response = self.client.get("/api/logs/paperless/")
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
def test_get_log(self):
|
|
log_data = "test\ntest2\n"
|
|
with open(os.path.join(settings.LOGGING_DIR, "paperless.log"), "w") as f:
|
|
f.write(log_data)
|
|
response = self.client.get("/api/logs/paperless/")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertListEqual(response.data, ["test", "test2"])
|
|
|
|
def test_invalid_regex_other_algorithm(self):
|
|
for endpoint in ["correspondents", "tags", "document_types"]:
|
|
response = self.client.post(
|
|
f"/api/{endpoint}/",
|
|
{
|
|
"name": "test",
|
|
"matching_algorithm": MatchingModel.MATCH_ANY,
|
|
"match": "[",
|
|
},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED, endpoint)
|
|
|
|
def test_invalid_regex(self):
|
|
for endpoint in ["correspondents", "tags", "document_types"]:
|
|
response = self.client.post(
|
|
f"/api/{endpoint}/",
|
|
{
|
|
"name": "test",
|
|
"matching_algorithm": MatchingModel.MATCH_REGEX,
|
|
"match": "[",
|
|
},
|
|
format="json",
|
|
)
|
|
self.assertEqual(
|
|
response.status_code,
|
|
status.HTTP_400_BAD_REQUEST,
|
|
endpoint,
|
|
)
|
|
|
|
def test_valid_regex(self):
|
|
for endpoint in ["correspondents", "tags", "document_types"]:
|
|
response = self.client.post(
|
|
f"/api/{endpoint}/",
|
|
{
|
|
"name": "test",
|
|
"matching_algorithm": MatchingModel.MATCH_REGEX,
|
|
"match": "[0-9]",
|
|
},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED, endpoint)
|
|
|
|
def test_regex_no_algorithm(self):
|
|
for endpoint in ["correspondents", "tags", "document_types"]:
|
|
response = self.client.post(
|
|
f"/api/{endpoint}/",
|
|
{"name": "test", "match": "[0-9]"},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED, endpoint)
|
|
|
|
def test_tag_color_default(self):
|
|
response = self.client.post("/api/tags/", {"name": "tag"}, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
self.assertEqual(Tag.objects.get(id=response.data["id"]).color, "#a6cee3")
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{response.data['id']}/", format="json").data[
|
|
"colour"
|
|
],
|
|
1,
|
|
)
|
|
|
|
def test_tag_color(self):
|
|
response = self.client.post(
|
|
"/api/tags/",
|
|
{"name": "tag", "colour": 3},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
self.assertEqual(Tag.objects.get(id=response.data["id"]).color, "#b2df8a")
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{response.data['id']}/", format="json").data[
|
|
"colour"
|
|
],
|
|
3,
|
|
)
|
|
|
|
def test_tag_color_invalid(self):
|
|
response = self.client.post(
|
|
"/api/tags/",
|
|
{"name": "tag", "colour": 34},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
def test_tag_color_custom(self):
|
|
tag = Tag.objects.create(name="test", color="#abcdef")
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{tag.id}/", format="json").data["colour"],
|
|
1,
|
|
)
|
|
|
|
def test_get_existing_notes(self):
|
|
"""
|
|
GIVEN:
|
|
- A document with a single note
|
|
WHEN:
|
|
- API reuqest for document notes is made
|
|
THEN:
|
|
- The associated note is returned
|
|
"""
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have notes!",
|
|
)
|
|
note = Note.objects.create(
|
|
note="This is a note.",
|
|
document=doc,
|
|
user=self.user,
|
|
)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/{doc.pk}/notes/",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
resp_data = response.json()
|
|
|
|
self.assertEqual(len(resp_data), 1)
|
|
|
|
resp_data = resp_data[0]
|
|
del resp_data["created"]
|
|
|
|
self.assertDictEqual(
|
|
resp_data,
|
|
{
|
|
"id": note.id,
|
|
"note": note.note,
|
|
"user": {
|
|
"id": note.user.id,
|
|
"username": note.user.username,
|
|
"first_name": note.user.first_name,
|
|
"last_name": note.user.last_name,
|
|
},
|
|
},
|
|
)
|
|
|
|
def test_create_note(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing document
|
|
WHEN:
|
|
- API request is made to add a note
|
|
THEN:
|
|
- note is created and associated with document, modified time is updated
|
|
"""
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have notes added",
|
|
created=timezone.now() - timedelta(days=1),
|
|
)
|
|
# set to yesterday
|
|
doc.modified = timezone.now() - timedelta(days=1)
|
|
self.assertEqual(doc.modified.day, (timezone.now() - timedelta(days=1)).day)
|
|
|
|
resp = self.client.post(
|
|
f"/api/documents/{doc.pk}/notes/",
|
|
data={"note": "this is a posted note"},
|
|
)
|
|
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/{doc.pk}/notes/",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
resp_data = response.json()
|
|
|
|
self.assertEqual(len(resp_data), 1)
|
|
|
|
resp_data = resp_data[0]
|
|
|
|
self.assertEqual(resp_data["note"], "this is a posted note")
|
|
|
|
doc = Document.objects.get(pk=doc.pk)
|
|
# modified was updated to today
|
|
self.assertEqual(doc.modified.day, timezone.now().day)
|
|
|
|
def test_notes_permissions_aware(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing document owned by user2 but with granted view perms for user1
|
|
WHEN:
|
|
- API request is made by user1 to add a note or delete
|
|
THEN:
|
|
- Notes are neither created nor deleted
|
|
"""
|
|
user1 = User.objects.create_user(username="test1")
|
|
user1.user_permissions.add(*Permission.objects.all())
|
|
user1.save()
|
|
|
|
user2 = User.objects.create_user(username="test2")
|
|
user2.save()
|
|
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have notes added",
|
|
)
|
|
doc.owner = user2
|
|
doc.save()
|
|
|
|
self.client.force_authenticate(user1)
|
|
|
|
resp = self.client.get(
|
|
f"/api/documents/{doc.pk}/notes/",
|
|
format="json",
|
|
)
|
|
self.assertEqual(resp.content, b"Insufficient permissions to view notes")
|
|
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
assign_perm("view_document", user1, doc)
|
|
|
|
resp = self.client.post(
|
|
f"/api/documents/{doc.pk}/notes/",
|
|
data={"note": "this is a posted note"},
|
|
)
|
|
self.assertEqual(resp.content, b"Insufficient permissions to create notes")
|
|
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
note = Note.objects.create(
|
|
note="This is a note.",
|
|
document=doc,
|
|
user=user2,
|
|
)
|
|
|
|
response = self.client.delete(
|
|
f"/api/documents/{doc.pk}/notes/?id={note.pk}",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.content, b"Insufficient permissions to delete notes")
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
def test_delete_note(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing document, existing note
|
|
WHEN:
|
|
- API request is made to delete a note
|
|
THEN:
|
|
- note is deleted, document modified is updated
|
|
"""
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have notes!",
|
|
created=timezone.now() - timedelta(days=1),
|
|
)
|
|
# set to yesterday
|
|
doc.modified = timezone.now() - timedelta(days=1)
|
|
self.assertEqual(doc.modified.day, (timezone.now() - timedelta(days=1)).day)
|
|
note = Note.objects.create(
|
|
note="This is a note.",
|
|
document=doc,
|
|
user=self.user,
|
|
)
|
|
|
|
response = self.client.delete(
|
|
f"/api/documents/{doc.pk}/notes/?id={note.pk}",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.assertEqual(len(Note.objects.all()), 0)
|
|
doc = Document.objects.get(pk=doc.pk)
|
|
# modified was updated to today
|
|
self.assertEqual(doc.modified.day, timezone.now().day)
|
|
|
|
def test_get_notes_no_doc(self):
|
|
"""
|
|
GIVEN:
|
|
- A request to get notes from a non-existent document
|
|
WHEN:
|
|
- API request for document notes is made
|
|
THEN:
|
|
- HTTP status.HTTP_404_NOT_FOUND is returned
|
|
"""
|
|
response = self.client.get(
|
|
"/api/documents/500/notes/",
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
def test_tag_unique_name_and_owner(self):
|
|
"""
|
|
GIVEN:
|
|
- Multiple users
|
|
- Tags owned by particular users
|
|
WHEN:
|
|
- API request for creating items which are unique by name and owner
|
|
THEN:
|
|
- Unique items are created
|
|
- Non-unique items are not allowed
|
|
"""
|
|
user1 = User.objects.create_user(username="test1")
|
|
user1.user_permissions.add(*Permission.objects.filter(codename="add_tag"))
|
|
user1.save()
|
|
|
|
user2 = User.objects.create_user(username="test2")
|
|
user2.user_permissions.add(*Permission.objects.filter(codename="add_tag"))
|
|
user2.save()
|
|
|
|
# User 1 creates tag 1 owned by user 1 by default
|
|
# No issue
|
|
self.client.force_authenticate(user1)
|
|
response = self.client.post("/api/tags/", {"name": "tag 1"}, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
|
|
# User 2 creates tag 1 owned by user 2 by default
|
|
# No issue
|
|
self.client.force_authenticate(user2)
|
|
response = self.client.post("/api/tags/", {"name": "tag 1"}, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
|
|
# User 2 creates tag 2 owned by user 1
|
|
# No issue
|
|
self.client.force_authenticate(user2)
|
|
response = self.client.post(
|
|
"/api/tags/",
|
|
{"name": "tag 2", "owner": user1.pk},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
|
|
# User 1 creates tag 2 owned by user 1 by default
|
|
# Not allowed, would create tag2/user1 which already exists
|
|
self.client.force_authenticate(user1)
|
|
response = self.client.post(
|
|
"/api/tags/",
|
|
{"name": "tag 2"},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
# User 1 creates tag 2 owned by user 1
|
|
# Not allowed, would create tag2/user1 which already exists
|
|
response = self.client.post(
|
|
"/api/tags/",
|
|
{"name": "tag 2", "owner": user1.pk},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
def test_tag_unique_name_and_owner_enforced_on_update(self):
|
|
"""
|
|
GIVEN:
|
|
- Multiple users
|
|
- Tags owned by particular users
|
|
WHEN:
|
|
- API request for to update tag in such as way as makes it non-unqiue
|
|
THEN:
|
|
- Unique items are created
|
|
- Non-unique items are not allowed on update
|
|
"""
|
|
user1 = User.objects.create_user(username="test1")
|
|
user1.user_permissions.add(*Permission.objects.filter(codename="change_tag"))
|
|
user1.save()
|
|
|
|
user2 = User.objects.create_user(username="test2")
|
|
user2.user_permissions.add(*Permission.objects.filter(codename="change_tag"))
|
|
user2.save()
|
|
|
|
# Create name tag 1 owned by user 1
|
|
# Create name tag 1 owned by user 2
|
|
Tag.objects.create(name="tag 1", owner=user1)
|
|
tag2 = Tag.objects.create(name="tag 1", owner=user2)
|
|
|
|
# User 2 attempts to change the owner of tag to user 1
|
|
# Not allowed, would change to tag1/user1 which already exists
|
|
self.client.force_authenticate(user2)
|
|
response = self.client.patch(
|
|
f"/api/tags/{tag2.id}/",
|
|
{"owner": user1.pk},
|
|
format="json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
def test_create_share_links(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing document
|
|
WHEN:
|
|
- API request is made to generate a share_link
|
|
- API request is made to view share_links on incorrect doc pk
|
|
- Invalid method request is made to view share_links doc
|
|
THEN:
|
|
- Link is created with a slug and associated with document
|
|
- 404
|
|
- Error
|
|
"""
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have notes added",
|
|
)
|
|
# never expires
|
|
resp = self.client.post(
|
|
"/api/share_links/",
|
|
data={
|
|
"document": doc.pk,
|
|
},
|
|
)
|
|
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
|
|
|
resp = self.client.post(
|
|
"/api/share_links/",
|
|
data={
|
|
"expiration": (timezone.now() + timedelta(days=7)).isoformat(),
|
|
"document": doc.pk,
|
|
"file_version": "original",
|
|
},
|
|
)
|
|
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
|
|
|
response = self.client.get(
|
|
f"/api/documents/{doc.pk}/share_links/",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
resp_data = response.json()
|
|
|
|
self.assertEqual(len(resp_data), 2)
|
|
|
|
self.assertGreater(len(resp_data[1]["slug"]), 0)
|
|
self.assertIsNone(resp_data[1]["expiration"])
|
|
self.assertEqual(
|
|
(parser.isoparse(resp_data[0]["expiration"]) - timezone.now()).days,
|
|
6,
|
|
)
|
|
|
|
sl1 = ShareLink.objects.get(slug=resp_data[1]["slug"])
|
|
self.assertEqual(str(sl1), f"Share Link for {doc.title}")
|
|
|
|
response = self.client.post(
|
|
f"/api/documents/{doc.pk}/share_links/",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
|
|
|
|
response = self.client.get(
|
|
"/api/documents/99/share_links/",
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
|
|
|
def test_share_links_permissions_aware(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing document owned by user2 but with granted view perms for user1
|
|
WHEN:
|
|
- API request is made by user1 to view share links
|
|
THEN:
|
|
- Links only shown if user has permissions
|
|
"""
|
|
user1 = User.objects.create_user(username="test1")
|
|
user1.user_permissions.add(*Permission.objects.all())
|
|
user1.save()
|
|
|
|
user2 = User.objects.create_user(username="test2")
|
|
user2.save()
|
|
|
|
doc = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document which will have share links added",
|
|
)
|
|
doc.owner = user2
|
|
doc.save()
|
|
|
|
self.client.force_authenticate(user1)
|
|
|
|
resp = self.client.get(
|
|
f"/api/documents/{doc.pk}/share_links/",
|
|
format="json",
|
|
)
|
|
self.assertEqual(resp.content, b"Insufficient permissions to add share link")
|
|
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
assign_perm("change_document", user1, doc)
|
|
|
|
resp = self.client.get(
|
|
f"/api/documents/{doc.pk}/share_links/",
|
|
format="json",
|
|
)
|
|
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
|
|
|
def test_next_asn(self):
|
|
"""
|
|
GIVEN:
|
|
- Existing documents with ASNs, highest owned by user2
|
|
WHEN:
|
|
- API request is made by user1 to get next ASN
|
|
THEN:
|
|
- ASN +1 from user2's doc is returned for user1
|
|
"""
|
|
user1 = User.objects.create_user(username="test1")
|
|
user1.user_permissions.add(*Permission.objects.all())
|
|
user1.save()
|
|
|
|
user2 = User.objects.create_user(username="test2")
|
|
user2.save()
|
|
|
|
doc1 = Document.objects.create(
|
|
title="test",
|
|
mime_type="application/pdf",
|
|
content="this is a document 1",
|
|
checksum="1",
|
|
archive_serial_number=998,
|
|
)
|
|
doc1.owner = user1
|
|
doc1.save()
|
|
|
|
doc2 = Document.objects.create(
|
|
title="test2",
|
|
mime_type="application/pdf",
|
|
content="this is a document 2 with higher ASN",
|
|
checksum="2",
|
|
archive_serial_number=999,
|
|
)
|
|
doc2.owner = user2
|
|
doc2.save()
|
|
|
|
self.client.force_authenticate(user1)
|
|
|
|
resp = self.client.get(
|
|
"/api/documents/next_asn/",
|
|
)
|
|
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(resp.content, b"1000")
|
|
|
|
|
|
class TestDocumentApiV2(DirectoriesMixin, APITestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = User.objects.create_superuser(username="temp_admin")
|
|
|
|
self.client.force_authenticate(user=self.user)
|
|
self.client.defaults["HTTP_ACCEPT"] = "application/json; version=2"
|
|
|
|
def test_tag_validate_color(self):
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/tags/",
|
|
{"name": "test", "color": "#12fFaA"},
|
|
format="json",
|
|
).status_code,
|
|
status.HTTP_201_CREATED,
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/tags/",
|
|
{"name": "test1", "color": "abcdef"},
|
|
format="json",
|
|
).status_code,
|
|
status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/tags/",
|
|
{"name": "test2", "color": "#abcdfg"},
|
|
format="json",
|
|
).status_code,
|
|
status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/tags/",
|
|
{"name": "test3", "color": "#asd"},
|
|
format="json",
|
|
).status_code,
|
|
status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
self.assertEqual(
|
|
self.client.post(
|
|
"/api/tags/",
|
|
{"name": "test4", "color": "#12121212"},
|
|
format="json",
|
|
).status_code,
|
|
status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
|
|
def test_tag_text_color(self):
|
|
t = Tag.objects.create(name="tag1", color="#000000")
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{t.id}/", format="json").data["text_color"],
|
|
"#ffffff",
|
|
)
|
|
|
|
t.color = "#ffffff"
|
|
t.save()
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{t.id}/", format="json").data["text_color"],
|
|
"#000000",
|
|
)
|
|
|
|
t.color = "asdf"
|
|
t.save()
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{t.id}/", format="json").data["text_color"],
|
|
"#000000",
|
|
)
|
|
|
|
t.color = "123"
|
|
t.save()
|
|
self.assertEqual(
|
|
self.client.get(f"/api/tags/{t.id}/", format="json").data["text_color"],
|
|
"#000000",
|
|
)
|
|
|
|
|
|
class TestApiUiSettings(DirectoriesMixin, APITestCase):
|
|
ENDPOINT = "/api/ui_settings/"
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.test_user = User.objects.create_superuser(username="test")
|
|
self.test_user.first_name = "Test"
|
|
self.test_user.last_name = "User"
|
|
self.test_user.save()
|
|
self.client.force_authenticate(user=self.test_user)
|
|
|
|
def test_api_get_ui_settings(self):
|
|
response = self.client.get(self.ENDPOINT, format="json")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data["user"],
|
|
{
|
|
"id": self.test_user.id,
|
|
"username": self.test_user.username,
|
|
"is_superuser": True,
|
|
"groups": [],
|
|
"first_name": self.test_user.first_name,
|
|
"last_name": self.test_user.last_name,
|
|
},
|
|
)
|
|
self.assertDictEqual(
|
|
response.data["settings"],
|
|
{
|
|
"update_checking": {
|
|
"backend_setting": "default",
|
|
},
|
|
},
|
|
)
|
|
|
|
def test_api_set_ui_settings(self):
|
|
settings = {
|
|
"settings": {
|
|
"dark_mode": {
|
|
"enabled": True,
|
|
},
|
|
},
|
|
}
|
|
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(settings),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
ui_settings = self.test_user.ui_settings
|
|
self.assertDictEqual(
|
|
ui_settings.settings,
|
|
settings["settings"],
|
|
)
|
|
|
|
|
|
class TestBulkDownload(DirectoriesMixin, APITestCase):
|
|
ENDPOINT = "/api/documents/bulk_download/"
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
user = User.objects.create_superuser(username="temp_admin")
|
|
self.client.force_authenticate(user=user)
|
|
|
|
self.doc1 = Document.objects.create(title="unrelated", checksum="A")
|
|
self.doc2 = Document.objects.create(
|
|
title="document A",
|
|
filename="docA.pdf",
|
|
mime_type="application/pdf",
|
|
checksum="B",
|
|
created=timezone.make_aware(datetime.datetime(2021, 1, 1)),
|
|
)
|
|
self.doc2b = Document.objects.create(
|
|
title="document A",
|
|
filename="docA2.pdf",
|
|
mime_type="application/pdf",
|
|
checksum="D",
|
|
created=timezone.make_aware(datetime.datetime(2021, 1, 1)),
|
|
)
|
|
self.doc3 = Document.objects.create(
|
|
title="document B",
|
|
filename="docB.jpg",
|
|
mime_type="image/jpeg",
|
|
checksum="C",
|
|
created=timezone.make_aware(datetime.datetime(2020, 3, 21)),
|
|
archive_filename="docB.pdf",
|
|
archive_checksum="D",
|
|
)
|
|
|
|
shutil.copy(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"),
|
|
self.doc2.source_path,
|
|
)
|
|
shutil.copy(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.png"),
|
|
self.doc2b.source_path,
|
|
)
|
|
shutil.copy(
|
|
os.path.join(os.path.dirname(__file__), "samples", "simple.jpg"),
|
|
self.doc3.source_path,
|
|
)
|
|
shutil.copy(
|
|
os.path.join(os.path.dirname(__file__), "samples", "test_with_bom.pdf"),
|
|
self.doc3.archive_path,
|
|
)
|
|
|
|
def test_download_originals(self):
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{"documents": [self.doc2.id, self.doc3.id], "content": "originals"},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 2)
|
|
self.assertIn("2021-01-01 document A.pdf", zipf.namelist())
|
|
self.assertIn("2020-03-21 document B.jpg", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf"))
|
|
|
|
with self.doc3.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2020-03-21 document B.jpg"))
|
|
|
|
def test_download_default(self):
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps({"documents": [self.doc2.id, self.doc3.id]}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 2)
|
|
self.assertIn("2021-01-01 document A.pdf", zipf.namelist())
|
|
self.assertIn("2020-03-21 document B.pdf", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf"))
|
|
|
|
with self.doc3.archive_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2020-03-21 document B.pdf"))
|
|
|
|
def test_download_both(self):
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps({"documents": [self.doc2.id, self.doc3.id], "content": "both"}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 3)
|
|
self.assertIn("originals/2021-01-01 document A.pdf", zipf.namelist())
|
|
self.assertIn("archive/2020-03-21 document B.pdf", zipf.namelist())
|
|
self.assertIn("originals/2020-03-21 document B.jpg", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("originals/2021-01-01 document A.pdf"),
|
|
)
|
|
|
|
with self.doc3.archive_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("archive/2020-03-21 document B.pdf"),
|
|
)
|
|
|
|
with self.doc3.source_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("originals/2020-03-21 document B.jpg"),
|
|
)
|
|
|
|
def test_filename_clashes(self):
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps({"documents": [self.doc2.id, self.doc2b.id]}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 2)
|
|
|
|
self.assertIn("2021-01-01 document A.pdf", zipf.namelist())
|
|
self.assertIn("2021-01-01 document A_01.pdf", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf"))
|
|
|
|
with self.doc2b.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("2021-01-01 document A_01.pdf"))
|
|
|
|
def test_compression(self):
|
|
self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{"documents": [self.doc2.id, self.doc2b.id], "compression": "lzma"},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
@override_settings(FILENAME_FORMAT="{correspondent}/{title}")
|
|
def test_formatted_download_originals(self):
|
|
"""
|
|
GIVEN:
|
|
- Defined file naming format
|
|
WHEN:
|
|
- Bulk download request for original documents
|
|
- Bulk download request requests to follow format
|
|
THEN:
|
|
- Files defined in resulting zipfile are formatted
|
|
"""
|
|
|
|
c = Correspondent.objects.create(name="test")
|
|
c2 = Correspondent.objects.create(name="a space name")
|
|
|
|
self.doc2.correspondent = c
|
|
self.doc2.title = "This is Doc 2"
|
|
self.doc2.save()
|
|
|
|
self.doc3.correspondent = c2
|
|
self.doc3.title = "Title 2 - Doc 3"
|
|
self.doc3.save()
|
|
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"documents": [self.doc2.id, self.doc3.id],
|
|
"content": "originals",
|
|
"follow_formatting": True,
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 2)
|
|
self.assertIn("a space name/Title 2 - Doc 3.jpg", zipf.namelist())
|
|
self.assertIn("test/This is Doc 2.pdf", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("test/This is Doc 2.pdf"))
|
|
|
|
with self.doc3.source_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("a space name/Title 2 - Doc 3.jpg"),
|
|
)
|
|
|
|
@override_settings(FILENAME_FORMAT="somewhere/{title}")
|
|
def test_formatted_download_archive(self):
|
|
"""
|
|
GIVEN:
|
|
- Defined file naming format
|
|
WHEN:
|
|
- Bulk download request for archive documents
|
|
- Bulk download request requests to follow format
|
|
THEN:
|
|
- Files defined in resulting zipfile are formatted
|
|
"""
|
|
|
|
self.doc2.title = "This is Doc 2"
|
|
self.doc2.save()
|
|
|
|
self.doc3.title = "Title 2 - Doc 3"
|
|
self.doc3.save()
|
|
print(self.doc3.archive_path)
|
|
print(self.doc3.archive_filename)
|
|
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"documents": [self.doc2.id, self.doc3.id],
|
|
"follow_formatting": True,
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 2)
|
|
self.assertIn("somewhere/This is Doc 2.pdf", zipf.namelist())
|
|
self.assertIn("somewhere/Title 2 - Doc 3.pdf", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(f.read(), zipf.read("somewhere/This is Doc 2.pdf"))
|
|
|
|
with self.doc3.archive_file as f:
|
|
self.assertEqual(f.read(), zipf.read("somewhere/Title 2 - Doc 3.pdf"))
|
|
|
|
@override_settings(FILENAME_FORMAT="{document_type}/{title}")
|
|
def test_formatted_download_both(self):
|
|
"""
|
|
GIVEN:
|
|
- Defined file naming format
|
|
WHEN:
|
|
- Bulk download request for original documents and archive documents
|
|
- Bulk download request requests to follow format
|
|
THEN:
|
|
- Files defined in resulting zipfile are formatted
|
|
"""
|
|
|
|
dc1 = DocumentType.objects.create(name="bill")
|
|
dc2 = DocumentType.objects.create(name="statement")
|
|
|
|
self.doc2.document_type = dc1
|
|
self.doc2.title = "This is Doc 2"
|
|
self.doc2.save()
|
|
|
|
self.doc3.document_type = dc2
|
|
self.doc3.title = "Title 2 - Doc 3"
|
|
self.doc3.save()
|
|
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"documents": [self.doc2.id, self.doc3.id],
|
|
"content": "both",
|
|
"follow_formatting": True,
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response["Content-Type"], "application/zip")
|
|
|
|
with zipfile.ZipFile(io.BytesIO(response.content)) as zipf:
|
|
self.assertEqual(len(zipf.filelist), 3)
|
|
self.assertIn("originals/bill/This is Doc 2.pdf", zipf.namelist())
|
|
self.assertIn("archive/statement/Title 2 - Doc 3.pdf", zipf.namelist())
|
|
self.assertIn("originals/statement/Title 2 - Doc 3.jpg", zipf.namelist())
|
|
|
|
with self.doc2.source_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("originals/bill/This is Doc 2.pdf"),
|
|
)
|
|
|
|
with self.doc3.archive_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("archive/statement/Title 2 - Doc 3.pdf"),
|
|
)
|
|
|
|
with self.doc3.source_file as f:
|
|
self.assertEqual(
|
|
f.read(),
|
|
zipf.read("originals/statement/Title 2 - Doc 3.jpg"),
|
|
)
|
|
|
|
|
|
class TestApiRemoteVersion(DirectoriesMixin, APITestCase):
|
|
ENDPOINT = "/api/remote_version/"
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
@mock.patch("urllib.request.urlopen")
|
|
def test_remote_version_enabled_no_update_prefix(self, urlopen_mock):
|
|
cm = MagicMock()
|
|
cm.getcode.return_value = status.HTTP_200_OK
|
|
cm.read.return_value = json.dumps({"tag_name": "ngx-1.6.0"}).encode()
|
|
cm.__enter__.return_value = cm
|
|
urlopen_mock.return_value = cm
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data,
|
|
{
|
|
"version": "1.6.0",
|
|
"update_available": False,
|
|
},
|
|
)
|
|
|
|
@mock.patch("urllib.request.urlopen")
|
|
def test_remote_version_enabled_no_update_no_prefix(self, urlopen_mock):
|
|
cm = MagicMock()
|
|
cm.getcode.return_value = status.HTTP_200_OK
|
|
cm.read.return_value = json.dumps(
|
|
{"tag_name": version.__full_version_str__},
|
|
).encode()
|
|
cm.__enter__.return_value = cm
|
|
urlopen_mock.return_value = cm
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data,
|
|
{
|
|
"version": version.__full_version_str__,
|
|
"update_available": False,
|
|
},
|
|
)
|
|
|
|
@mock.patch("urllib.request.urlopen")
|
|
def test_remote_version_enabled_update(self, urlopen_mock):
|
|
new_version = (
|
|
version.__version__[0],
|
|
version.__version__[1],
|
|
version.__version__[2] + 1,
|
|
)
|
|
new_version_str = ".".join(map(str, new_version))
|
|
|
|
cm = MagicMock()
|
|
cm.getcode.return_value = status.HTTP_200_OK
|
|
cm.read.return_value = json.dumps(
|
|
{"tag_name": new_version_str},
|
|
).encode()
|
|
cm.__enter__.return_value = cm
|
|
urlopen_mock.return_value = cm
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data,
|
|
{
|
|
"version": new_version_str,
|
|
"update_available": True,
|
|
},
|
|
)
|
|
|
|
@mock.patch("urllib.request.urlopen")
|
|
def test_remote_version_bad_json(self, urlopen_mock):
|
|
cm = MagicMock()
|
|
cm.getcode.return_value = status.HTTP_200_OK
|
|
cm.read.return_value = b'{ "blah":'
|
|
cm.__enter__.return_value = cm
|
|
urlopen_mock.return_value = cm
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data,
|
|
{
|
|
"version": "0.0.0",
|
|
"update_available": False,
|
|
},
|
|
)
|
|
|
|
@mock.patch("urllib.request.urlopen")
|
|
def test_remote_version_exception(self, urlopen_mock):
|
|
cm = MagicMock()
|
|
cm.getcode.return_value = status.HTTP_200_OK
|
|
cm.read.side_effect = urllib.error.URLError("an error")
|
|
cm.__enter__.return_value = cm
|
|
urlopen_mock.return_value = cm
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertDictEqual(
|
|
response.data,
|
|
{
|
|
"version": "0.0.0",
|
|
"update_available": False,
|
|
},
|
|
)
|
|
|
|
|
|
class TestApiObjects(DirectoriesMixin, APITestCase):
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
|
|
user = User.objects.create_superuser(username="temp_admin")
|
|
self.client.force_authenticate(user=user)
|
|
|
|
self.tag1 = Tag.objects.create(name="t1", is_inbox_tag=True)
|
|
self.tag2 = Tag.objects.create(name="t2")
|
|
self.tag3 = Tag.objects.create(name="t3")
|
|
self.c1 = Correspondent.objects.create(name="c1")
|
|
self.c2 = Correspondent.objects.create(name="c2")
|
|
self.c3 = Correspondent.objects.create(name="c3")
|
|
self.dt1 = DocumentType.objects.create(name="dt1")
|
|
self.dt2 = DocumentType.objects.create(name="dt2")
|
|
self.sp1 = StoragePath.objects.create(name="sp1", path="Something/{title}")
|
|
self.sp2 = StoragePath.objects.create(name="sp2", path="Something2/{title}")
|
|
|
|
def test_object_filters(self):
|
|
response = self.client.get(
|
|
f"/api/tags/?id={self.tag2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
|
|
response = self.client.get(
|
|
f"/api/tags/?id__in={self.tag1.id},{self.tag3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
response = self.client.get(
|
|
f"/api/correspondents/?id={self.c2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
|
|
response = self.client.get(
|
|
f"/api/correspondents/?id__in={self.c1.id},{self.c3.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
response = self.client.get(
|
|
f"/api/document_types/?id={self.dt1.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
|
|
response = self.client.get(
|
|
f"/api/document_types/?id__in={self.dt1.id},{self.dt2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
response = self.client.get(
|
|
f"/api/storage_paths/?id={self.sp1.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 1)
|
|
|
|
response = self.client.get(
|
|
f"/api/storage_paths/?id__in={self.sp1.id},{self.sp2.id}",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
results = response.data["results"]
|
|
self.assertEqual(len(results), 2)
|
|
|
|
|
|
class TestApiStoragePaths(DirectoriesMixin, APITestCase):
|
|
ENDPOINT = "/api/storage_paths/"
|
|
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
|
|
user = User.objects.create_superuser(username="temp_admin")
|
|
self.client.force_authenticate(user=user)
|
|
|
|
self.sp1 = StoragePath.objects.create(name="sp1", path="Something/{checksum}")
|
|
|
|
def test_api_get_storage_path(self):
|
|
"""
|
|
GIVEN:
|
|
- API request to get all storage paths
|
|
WHEN:
|
|
- API is called
|
|
THEN:
|
|
- Existing storage paths are returned
|
|
"""
|
|
response = self.client.get(self.ENDPOINT, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["count"], 1)
|
|
|
|
resp_storage_path = response.data["results"][0]
|
|
self.assertEqual(resp_storage_path["id"], self.sp1.id)
|
|
self.assertEqual(resp_storage_path["path"], self.sp1.path)
|
|
|
|
def test_api_create_storage_path(self):
|
|
"""
|
|
GIVEN:
|
|
- API request to create a storage paths
|
|
WHEN:
|
|
- API is called
|
|
THEN:
|
|
- Correct HTTP response
|
|
- New storage path is created
|
|
"""
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"name": "A storage path",
|
|
"path": "Somewhere/{asn}",
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
self.assertEqual(StoragePath.objects.count(), 2)
|
|
|
|
def test_api_create_invalid_storage_path(self):
|
|
"""
|
|
GIVEN:
|
|
- API request to create a storage paths
|
|
- Storage path format is incorrect
|
|
WHEN:
|
|
- API is called
|
|
THEN:
|
|
- Correct HTTP 400 response
|
|
- No storage path is created
|
|
"""
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"name": "Another storage path",
|
|
"path": "Somewhere/{correspdent}",
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
self.assertEqual(StoragePath.objects.count(), 1)
|
|
|
|
def test_api_storage_path_placeholders(self):
|
|
"""
|
|
GIVEN:
|
|
- API request to create a storage path with placeholders
|
|
- Storage path is valid
|
|
WHEN:
|
|
- API is called
|
|
THEN:
|
|
- Correct HTTP response
|
|
- New storage path is created
|
|
"""
|
|
response = self.client.post(
|
|
self.ENDPOINT,
|
|
json.dumps(
|
|
{
|
|
"name": "Storage path with placeholders",
|
|
"path": "{title}/{correspondent}/{document_type}/{created}/{created_year}"
|
|
"/{created_year_short}/{created_month}/{created_month_name}"
|
|
"/{created_month_name_short}/{created_day}/{added}/{added_year}"
|
|
"/{added_year_short}/{added_month}/{added_month_name}"
|
|
"/{added_month_name_short}/{added_day}/{asn}/{tags}"
|
|
"/{tag_list}/{owner_username}/{original_name}/{doc_pk}/",
|
|
},
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
|
self.assertEqual(StoragePath.objects.count(), 2)
|
|
|
|
@mock.patch("documents.bulk_edit.bulk_update_documents.delay")
|
|
def test_api_update_storage_path(self, bulk_update_mock):
|
|
"""
|
|
GIVEN:
|
|
- API request to get all storage paths
|
|
WHEN:
|
|
- API is called
|
|
THEN:
|
|
- Existing storage paths are returned
|
|
"""
|
|
document = Document.objects.create(
|
|
mime_type="application/pdf",
|
|
storage_path=self.sp1,
|
|
)
|
|
response = self.client.patch(
|
|
f"{self.ENDPOINT}{self.sp1.pk}/",
|
|
data={
|
|
"path": "somewhere/{created} - {title}",
|
|
},
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
bulk_update_mock.assert_called_once()
|
|
|
|
args, _ = bulk_update_mock.call_args
|
|
|
|
self.assertCountEqual([document.pk], args[0])
|
|
|
|
|
|
class TestTasks(DirectoriesMixin, APITestCase):
|
|
ENDPOINT = "/api/tasks/"
|
|
ENDPOINT_ACKNOWLEDGE = "/api/acknowledge_tasks/"
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = User.objects.create_superuser(username="temp_admin")
|
|
self.client.force_authenticate(user=self.user)
|
|
|
|
def test_get_tasks(self):
|
|
"""
|
|
GIVEN:
|
|
- Attempted celery tasks
|
|
WHEN:
|
|
- API call is made to get tasks
|
|
THEN:
|
|
- Attempting and pending tasks are serialized and provided
|
|
"""
|
|
|
|
task1 = PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_one.pdf",
|
|
)
|
|
|
|
task2 = PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_two.pdf",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 2)
|
|
returned_task1 = response.data[1]
|
|
returned_task2 = response.data[0]
|
|
|
|
self.assertEqual(returned_task1["task_id"], task1.task_id)
|
|
self.assertEqual(returned_task1["status"], celery.states.PENDING)
|
|
self.assertEqual(returned_task1["task_file_name"], task1.task_file_name)
|
|
|
|
self.assertEqual(returned_task2["task_id"], task2.task_id)
|
|
self.assertEqual(returned_task2["status"], celery.states.PENDING)
|
|
self.assertEqual(returned_task2["task_file_name"], task2.task_file_name)
|
|
|
|
def test_get_single_task_status(self):
|
|
"""
|
|
GIVEN
|
|
- Query parameter for a valid task ID
|
|
WHEN:
|
|
- API call is made to get task status
|
|
THEN:
|
|
- Single task data is returned
|
|
"""
|
|
|
|
id1 = str(uuid.uuid4())
|
|
task1 = PaperlessTask.objects.create(
|
|
task_id=id1,
|
|
task_file_name="task_one.pdf",
|
|
)
|
|
|
|
_ = PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_two.pdf",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT + f"?task_id={id1}")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 1)
|
|
returned_task1 = response.data[0]
|
|
|
|
self.assertEqual(returned_task1["task_id"], task1.task_id)
|
|
|
|
def test_get_single_task_status_not_valid(self):
|
|
"""
|
|
GIVEN
|
|
- Query parameter for a non-existent task ID
|
|
WHEN:
|
|
- API call is made to get task status
|
|
THEN:
|
|
- No task data is returned
|
|
"""
|
|
PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_one.pdf",
|
|
)
|
|
|
|
_ = PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_two.pdf",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT + "?task_id=bad-task-id")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 0)
|
|
|
|
def test_acknowledge_tasks(self):
|
|
"""
|
|
GIVEN:
|
|
- Attempted celery tasks
|
|
WHEN:
|
|
- API call is made to get mark task as acknowledged
|
|
THEN:
|
|
- Task is marked as acknowledged
|
|
"""
|
|
task = PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_one.pdf",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
self.assertEqual(len(response.data), 1)
|
|
|
|
response = self.client.post(
|
|
self.ENDPOINT_ACKNOWLEDGE,
|
|
{"tasks": [task.id]},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
self.assertEqual(len(response.data), 0)
|
|
|
|
def test_task_result_no_error(self):
|
|
"""
|
|
GIVEN:
|
|
- A celery task completed without error
|
|
WHEN:
|
|
- API call is made to get tasks
|
|
THEN:
|
|
- The returned data includes the task result
|
|
"""
|
|
PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_one.pdf",
|
|
status=celery.states.SUCCESS,
|
|
result="Success. New document id 1 created",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 1)
|
|
|
|
returned_data = response.data[0]
|
|
|
|
self.assertEqual(returned_data["result"], "Success. New document id 1 created")
|
|
self.assertEqual(returned_data["related_document"], "1")
|
|
|
|
def test_task_result_with_error(self):
|
|
"""
|
|
GIVEN:
|
|
- A celery task completed with an exception
|
|
WHEN:
|
|
- API call is made to get tasks
|
|
THEN:
|
|
- The returned result is the exception info
|
|
"""
|
|
PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="task_one.pdf",
|
|
status=celery.states.FAILURE,
|
|
result="test.pdf: Not consuming test.pdf: It is a duplicate.",
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 1)
|
|
|
|
returned_data = response.data[0]
|
|
|
|
self.assertEqual(
|
|
returned_data["result"],
|
|
"test.pdf: Not consuming test.pdf: It is a duplicate.",
|
|
)
|
|
|
|
def test_task_name_webui(self):
|
|
"""
|
|
GIVEN:
|
|
- Attempted celery task
|
|
- Task was created through the webui
|
|
WHEN:
|
|
- API call is made to get tasks
|
|
THEN:
|
|
- Returned data include the filename
|
|
"""
|
|
PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="test.pdf",
|
|
task_name="documents.tasks.some_task",
|
|
status=celery.states.SUCCESS,
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 1)
|
|
|
|
returned_data = response.data[0]
|
|
|
|
self.assertEqual(returned_data["task_file_name"], "test.pdf")
|
|
|
|
def test_task_name_consume_folder(self):
|
|
"""
|
|
GIVEN:
|
|
- Attempted celery task
|
|
- Task was created through the consume folder
|
|
WHEN:
|
|
- API call is made to get tasks
|
|
THEN:
|
|
- Returned data include the filename
|
|
"""
|
|
PaperlessTask.objects.create(
|
|
task_id=str(uuid.uuid4()),
|
|
task_file_name="anothertest.pdf",
|
|
task_name="documents.tasks.some_task",
|
|
status=celery.states.SUCCESS,
|
|
)
|
|
|
|
response = self.client.get(self.ENDPOINT)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.data), 1)
|
|
|
|
returned_data = response.data[0]
|
|
|
|
self.assertEqual(returned_data["task_file_name"], "anothertest.pdf")
|