mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-08-30 14:37:14 +00:00
Gotenberg's LibreOffice route enables updateIndexes by default, which refreshes dynamic fields (e.g. auto-dates) to the current date. Disable it so field values are preserved as authored.
234 lines
7.1 KiB
Python
234 lines
7.1 KiB
Python
import datetime
|
|
import zoneinfo
|
|
from http import HTTPStatus
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from httpx import codes
|
|
from pytest_django.fixtures import SettingsWrapper
|
|
from pytest_httpx import HTTPXMock
|
|
|
|
from documents.parsers import ParseError
|
|
from paperless.parsers import ParserContext
|
|
from paperless.parsers import ParserProtocol
|
|
from paperless.parsers.tika import TikaDocumentParser
|
|
|
|
|
|
class TestTikaParserRegistryInterface:
|
|
"""Verify that TikaDocumentParser satisfies the ParserProtocol contract."""
|
|
|
|
def test_satisfies_parser_protocol(self) -> None:
|
|
assert isinstance(TikaDocumentParser(), ParserProtocol)
|
|
|
|
def test_supported_mime_types_is_classmethod(self) -> None:
|
|
mime_types = TikaDocumentParser.supported_mime_types()
|
|
assert isinstance(mime_types, dict)
|
|
assert len(mime_types) > 0
|
|
|
|
def test_score_returns_none_when_tika_disabled(
|
|
self,
|
|
settings: SettingsWrapper,
|
|
) -> None:
|
|
settings.TIKA_ENABLED = False
|
|
result = TikaDocumentParser.score(
|
|
"application/vnd.oasis.opendocument.text",
|
|
"sample.odt",
|
|
)
|
|
assert result is None
|
|
|
|
def test_score_returns_int_when_tika_enabled(
|
|
self,
|
|
settings: SettingsWrapper,
|
|
) -> None:
|
|
settings.TIKA_ENABLED = True
|
|
result = TikaDocumentParser.score(
|
|
"application/vnd.oasis.opendocument.text",
|
|
"sample.odt",
|
|
)
|
|
assert isinstance(result, int)
|
|
|
|
def test_score_returns_none_for_unsupported_mime(
|
|
self,
|
|
settings: SettingsWrapper,
|
|
) -> None:
|
|
settings.TIKA_ENABLED = True
|
|
result = TikaDocumentParser.score("application/pdf", "doc.pdf")
|
|
assert result is None
|
|
|
|
def test_can_produce_archive_is_false(self) -> None:
|
|
assert TikaDocumentParser().can_produce_archive is False
|
|
|
|
def test_requires_pdf_rendition_is_true(self) -> None:
|
|
assert TikaDocumentParser().requires_pdf_rendition is True
|
|
|
|
def test_get_page_count_returns_none_without_archive(
|
|
self,
|
|
tika_parser: TikaDocumentParser,
|
|
sample_odt_file: Path,
|
|
) -> None:
|
|
assert (
|
|
tika_parser.get_page_count(
|
|
sample_odt_file,
|
|
"application/vnd.oasis.opendocument.text",
|
|
)
|
|
is None
|
|
)
|
|
|
|
def test_get_page_count_returns_int_with_pdf_archive(
|
|
self,
|
|
tika_parser: TikaDocumentParser,
|
|
simple_digital_pdf_file: Path,
|
|
) -> None:
|
|
tika_parser._archive_path = simple_digital_pdf_file
|
|
count = tika_parser.get_page_count(simple_digital_pdf_file, "application/pdf")
|
|
assert isinstance(count, int)
|
|
assert count > 0
|
|
|
|
|
|
@pytest.mark.django_db()
|
|
class TestTikaParser:
|
|
def test_parse(
|
|
self,
|
|
httpx_mock: HTTPXMock,
|
|
settings: SettingsWrapper,
|
|
tika_parser: TikaDocumentParser,
|
|
sample_odt_file: Path,
|
|
) -> None:
|
|
settings.TIME_ZONE = "America/Chicago"
|
|
# Pretend parse response
|
|
httpx_mock.add_response(
|
|
json={
|
|
"Content-Type": "application/vnd.oasis.opendocument.text",
|
|
"X-TIKA:Parsed-By": [],
|
|
"X-TIKA:content": "the content",
|
|
"dcterms:created": "2020-11-21T00:00:00",
|
|
},
|
|
)
|
|
# Pretend convert to PDF response
|
|
httpx_mock.add_response(content=b"PDF document")
|
|
|
|
tika_parser.configure(ParserContext())
|
|
tika_parser.parse(sample_odt_file, "application/vnd.oasis.opendocument.text")
|
|
|
|
assert tika_parser.get_text() == "the content"
|
|
assert tika_parser.get_archive_path() is not None
|
|
with Path(tika_parser.get_archive_path()).open("rb") as f:
|
|
assert f.read() == b"PDF document"
|
|
|
|
assert tika_parser.get_date() == datetime.datetime(
|
|
2020,
|
|
11,
|
|
21,
|
|
tzinfo=zoneinfo.ZoneInfo("America/Chicago"),
|
|
)
|
|
|
|
def test_metadata(
|
|
self,
|
|
httpx_mock: HTTPXMock,
|
|
tika_parser: TikaDocumentParser,
|
|
sample_odt_file: Path,
|
|
) -> None:
|
|
httpx_mock.add_response(
|
|
json={
|
|
"Content-Type": "application/vnd.oasis.opendocument.text",
|
|
"X-TIKA:Parsed-By": [],
|
|
"Some-key": "value",
|
|
"dcterms:created": "2020-11-21T00:00:00",
|
|
},
|
|
)
|
|
|
|
metadata = tika_parser.extract_metadata(
|
|
sample_odt_file,
|
|
"application/vnd.oasis.opendocument.text",
|
|
)
|
|
|
|
assert "dcterms:created" in [m["key"] for m in metadata]
|
|
assert "Some-key" in [m["key"] for m in metadata]
|
|
|
|
def test_convert_failure(
|
|
self,
|
|
httpx_mock: HTTPXMock,
|
|
tika_parser: TikaDocumentParser,
|
|
sample_odt_file: Path,
|
|
) -> None:
|
|
"""
|
|
GIVEN:
|
|
- Document needs to be converted to PDF
|
|
WHEN:
|
|
- Gotenberg server returns an error
|
|
THEN:
|
|
- Parse error is raised
|
|
"""
|
|
# Pretend convert to PDF response
|
|
httpx_mock.add_response(status_code=HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
|
|
with pytest.raises(ParseError):
|
|
tika_parser._convert_to_pdf(sample_odt_file)
|
|
|
|
@pytest.mark.parametrize(
|
|
("setting_value", "expected_form_value"),
|
|
[
|
|
("pdfa", "PDF/A-2b"),
|
|
("pdfa-1", "PDF/A-2b"),
|
|
("pdfa-2", "PDF/A-2b"),
|
|
("pdfa-3", "PDF/A-3b"),
|
|
],
|
|
)
|
|
def test_request_pdf_a_format(
|
|
self,
|
|
setting_value: str,
|
|
expected_form_value: str,
|
|
httpx_mock: HTTPXMock,
|
|
settings: SettingsWrapper,
|
|
sample_odt_file: Path,
|
|
) -> None:
|
|
"""
|
|
GIVEN:
|
|
- Document needs to be converted to PDF
|
|
WHEN:
|
|
- Specific PDF/A format requested
|
|
THEN:
|
|
- Request to Gotenberg contains the expected PDF/A format string
|
|
"""
|
|
# Parser must be created after the setting is changed so that
|
|
# OutputTypeConfig reads the correct value at __init__ time.
|
|
settings.OCR_OUTPUT_TYPE = setting_value
|
|
httpx_mock.add_response(
|
|
status_code=codes.OK,
|
|
content=b"PDF document",
|
|
method="POST",
|
|
)
|
|
|
|
with TikaDocumentParser() as parser:
|
|
parser._convert_to_pdf(sample_odt_file)
|
|
|
|
request = httpx_mock.get_request()
|
|
|
|
assert request is not None
|
|
|
|
expected_field_name = "pdfa"
|
|
|
|
content_type = request.headers["Content-Type"]
|
|
assert "multipart/form-data" in content_type
|
|
|
|
boundary = content_type.split("boundary=")[1]
|
|
|
|
parts = request.content.split(f"--{boundary}".encode())
|
|
|
|
form_field_found = any(
|
|
f'name="{expected_field_name}"'.encode() in part
|
|
and expected_form_value.encode() in part
|
|
for part in parts
|
|
)
|
|
|
|
assert form_field_found
|
|
|
|
# Field updates must be disabled so LibreOffice preserves document
|
|
# fields (e.g. auto-date fields) as authored rather than refreshing
|
|
# them to the current date.
|
|
assert any(
|
|
b'name="updateIndexes"' in part and b"false" in part for part in parts
|
|
)
|
|
|
|
httpx_mock.reset()
|