From 654ce5d8f3b17c2c31e504fe9b589ad778456c9e Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 10 Aug 2026 07:40:35 -0700 Subject: [PATCH] Backend tests --- src/documents/tests/test_api_app_config.py | 46 ++++++++++ .../tests/parsers/test_remote_parser.py | 17 ++++ src/paperless/tests/test_remote_ocr_config.py | 88 +++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 src/paperless/tests/test_remote_ocr_config.py diff --git a/src/documents/tests/test_api_app_config.py b/src/documents/tests/test_api_app_config.py index 63db4b766..d4cc4b6d2 100644 --- a/src/documents/tests/test_api_app_config.py +++ b/src/documents/tests/test_api_app_config.py @@ -72,6 +72,9 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase): "barcode_enable_tag": None, "barcode_tag_mapping": None, "barcode_tag_split": None, + "remote_ocr_engine": None, + "remote_ocr_api_key": None, + "remote_ocr_endpoint": None, "ai_enabled": False, "llm_embedding_backend": None, "llm_embedding_model": None, @@ -870,6 +873,49 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase): config.refresh_from_db() self.assertEqual(config.llm_api_key, None) + def test_update_remote_ocr_api_key(self) -> None: + """ + GIVEN: + - Existing config with remote_ocr_api_key specified + WHEN: + - API to update remote_ocr_api_key is called with all *s + - API to update remote_ocr_api_key is called with empty string + THEN: + - remote_ocr_api_key is unchanged + - remote_ocr_api_key is set to None + """ + config = ApplicationConfiguration.objects.first() + assert config is not None + config.remote_ocr_api_key = "1234567890" + config.save() + + # Test with all * + response = self.client.patch( + f"{self.ENDPOINT}1/", + json.dumps( + { + "remote_ocr_api_key": "*" * 32, + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + config.refresh_from_db() + self.assertEqual(config.remote_ocr_api_key, "1234567890") + # Test with empty string + response = self.client.patch( + f"{self.ENDPOINT}1/", + json.dumps( + { + "remote_ocr_api_key": "", + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + config.refresh_from_db() + self.assertEqual(config.remote_ocr_api_key, None) + def test_enable_ai_index_triggers_update(self) -> None: """ GIVEN: diff --git a/src/paperless/tests/parsers/test_remote_parser.py b/src/paperless/tests/parsers/test_remote_parser.py index 931ab3ec5..4b9c0bafc 100644 --- a/src/paperless/tests/parsers/test_remote_parser.py +++ b/src/paperless/tests/parsers/test_remote_parser.py @@ -21,6 +21,7 @@ from unittest.mock import Mock import pytest from documents.parsers import ParseError +from paperless.models import ApplicationConfiguration from paperless.parsers import ParserContext from paperless.parsers import ParserProtocol from paperless.parsers.remote import RemoteDocumentParser @@ -33,6 +34,10 @@ if TYPE_CHECKING: from pytest_mock import MockerFixture +# Remote ocr config from ApplicationConfiguration needs DB access +pytestmark = pytest.mark.django_db + + # --------------------------------------------------------------------------- # Module-local fixtures # --------------------------------------------------------------------------- @@ -227,6 +232,18 @@ class TestRemoteParserScore: score = RemoteDocumentParser.score("application/pdf", "doc.pdf") assert score is not None and score > 10 + @pytest.mark.usefixtures("no_engine_settings") + def test_score_uses_app_config_when_env_unset(self) -> None: + """The app config alone is enough to activate the parser.""" + config = ApplicationConfiguration.objects.first() + assert config is not None + config.remote_ocr_engine = "azureai" + config.remote_ocr_api_key = "app-config-key" + config.remote_ocr_endpoint = "https://config.cognitiveservices.azure.com" + config.save() + + assert RemoteDocumentParser.score("application/pdf", "doc.pdf") == 20 + # --------------------------------------------------------------------------- # Properties diff --git a/src/paperless/tests/test_remote_ocr_config.py b/src/paperless/tests/test_remote_ocr_config.py new file mode 100644 index 000000000..b331d41a9 --- /dev/null +++ b/src/paperless/tests/test_remote_ocr_config.py @@ -0,0 +1,88 @@ +"""Tests for RemoteOCRConfig precedence between app config and Django settings.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest +from django.test import override_settings + +from paperless.config import RemoteOCRConfig + +if TYPE_CHECKING: + from unittest.mock import MagicMock + + +@pytest.fixture() +def null_app_config(mocker) -> MagicMock: + """Mock ApplicationConfiguration with all fields None → falls back to Django settings.""" + return mocker.MagicMock( + remote_ocr_engine=None, + remote_ocr_api_key=None, + remote_ocr_endpoint=None, + ) + + +@pytest.fixture() +def make_remote_ocr_config(mocker): + def _make(app_config, **django_settings_overrides): + mocker.patch( + "paperless.config.BaseConfig._get_config_instance", + return_value=app_config, + ) + with override_settings(**django_settings_overrides): + return RemoteOCRConfig() + + return _make + + +class TestRemoteOCRConfig: + def test_falls_back_to_settings( + self, + make_remote_ocr_config, + null_app_config, + ) -> None: + cfg = make_remote_ocr_config( + null_app_config, + REMOTE_OCR_ENGINE="azureai", + REMOTE_OCR_API_KEY="env-key", + REMOTE_OCR_ENDPOINT="https://env.cognitiveservices.azure.com", + ) + assert cfg.remote_ocr_engine == "azureai" + assert cfg.remote_ocr_api_key == "env-key" + assert cfg.remote_ocr_endpoint == "https://env.cognitiveservices.azure.com" + + def test_app_config_takes_precedence( + self, + make_remote_ocr_config, + mocker, + ) -> None: + app_config = mocker.MagicMock( + remote_ocr_engine="azureai", + remote_ocr_api_key="db-key", + remote_ocr_endpoint="https://db.cognitiveservices.azure.com", + ) + cfg = make_remote_ocr_config( + app_config, + REMOTE_OCR_ENGINE=None, + REMOTE_OCR_API_KEY="env-key", + REMOTE_OCR_ENDPOINT="https://env.cognitiveservices.azure.com", + ) + assert cfg.remote_ocr_engine == "azureai" + assert cfg.remote_ocr_api_key == "db-key" + assert cfg.remote_ocr_endpoint == "https://db.cognitiveservices.azure.com" + + def test_unset_everywhere( + self, + make_remote_ocr_config, + null_app_config, + ) -> None: + cfg = make_remote_ocr_config( + null_app_config, + REMOTE_OCR_ENGINE=None, + REMOTE_OCR_API_KEY=None, + REMOTE_OCR_ENDPOINT=None, + ) + assert cfg.remote_ocr_engine is None + assert cfg.remote_ocr_api_key is None + assert cfg.remote_ocr_endpoint is None