From 8787d403f2d62cad0300d77758fa082db3a6751e Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:10:26 -0800 Subject: [PATCH] Chore: Split test_settings.py and add regression tests for Redis URL parsing - Migrate tests for custom parsers to test_custom_parsers.py - Add TestParseIgnoreDates and test_parse_dateparser_languages - Add regression test cases for Redis URLs with credentials (PR #12239) Co-Authored-By: Claude Sonnet 4.6 --- src/paperless/settings/__init__.py | 52 +-- src/paperless/settings/custom.py | 50 ++ .../tests/settings/test_custom_parsers.py | 103 +++++ src/paperless/tests/test_settings.py | 426 ------------------ 4 files changed, 157 insertions(+), 474 deletions(-) diff --git a/src/paperless/settings/__init__.py b/src/paperless/settings/__init__.py index a8956630b..da750fb5e 100644 --- a/src/paperless/settings/__init__.py +++ b/src/paperless/settings/__init__.py @@ -11,13 +11,14 @@ from typing import Final from urllib.parse import urlparse from compression_middleware.middleware import CompressionMiddleware -from dateparser.languages.loader import LocaleDataLoader from django.utils.translation import gettext_lazy as _ from dotenv import load_dotenv from paperless.settings.custom import parse_beat_schedule +from paperless.settings.custom import parse_dateparser_languages from paperless.settings.custom import parse_db_settings from paperless.settings.custom import parse_hosting_settings +from paperless.settings.custom import parse_ignore_dates from paperless.settings.custom import parse_redis_url from paperless.settings.parsers import get_bool_from_env from paperless.settings.parsers import get_float_from_env @@ -934,23 +935,9 @@ DATE_ORDER = os.getenv("PAPERLESS_DATE_ORDER", "DMY") FILENAME_DATE_ORDER = os.getenv("PAPERLESS_FILENAME_DATE_ORDER") -def _parse_dateparser_languages(languages: str | None): - language_list = languages.split("+") if languages else [] - # There is an unfixed issue in zh-Hant and zh-Hans locales in the dateparser lib. - # See: https://github.com/scrapinghub/dateparser/issues/875 - for index, language in enumerate(language_list): - if language.startswith("zh-") and "zh" not in language_list: - logger.warning( - f'Chinese locale detected: {language}. dateparser might fail to parse some dates with this locale, so Chinese ("zh") will be used as a fallback.', - ) - language_list.append("zh") - - return list(LocaleDataLoader().get_locale_map(locales=language_list)) - - # If not set, we will infer it at runtime DATE_PARSER_LANGUAGES = ( - _parse_dateparser_languages( + parse_dateparser_languages( os.getenv("PAPERLESS_DATE_PARSER_LANGUAGES"), ) if os.getenv("PAPERLESS_DATE_PARSER_LANGUAGES") @@ -996,42 +983,11 @@ if AUDIT_LOG_ENABLED: MIDDLEWARE.append("auditlog.middleware.AuditlogMiddleware") -def _parse_ignore_dates( - env_ignore: str, - date_order: str = DATE_ORDER, -) -> set[datetime.datetime]: - """ - If the PAPERLESS_IGNORE_DATES environment variable is set, parse the - user provided string(s) into dates - - Args: - env_ignore (str): The value of the environment variable, comma separated dates - date_order (str, optional): The format of the date strings. - Defaults to DATE_ORDER. - - Returns: - Set[datetime.datetime]: The set of parsed date objects - """ - import dateparser - - ignored_dates = set() - for s in env_ignore.split(","): - d = dateparser.parse( - s, - settings={ - "DATE_ORDER": date_order, - }, - ) - if d: - ignored_dates.add(d.date()) - return ignored_dates - - # List dates that should be ignored when trying to parse date from document text IGNORE_DATES: set[datetime.date] = set() if os.getenv("PAPERLESS_IGNORE_DATES") is not None: - IGNORE_DATES = _parse_ignore_dates(os.getenv("PAPERLESS_IGNORE_DATES")) + IGNORE_DATES = parse_ignore_dates(os.getenv("PAPERLESS_IGNORE_DATES"), DATE_ORDER) ENABLE_UPDATE_CHECK = os.getenv("PAPERLESS_ENABLE_UPDATE_CHECK", "default") if ENABLE_UPDATE_CHECK != "default": diff --git a/src/paperless/settings/custom.py b/src/paperless/settings/custom.py index e42af97c8..32b3aa364 100644 --- a/src/paperless/settings/custom.py +++ b/src/paperless/settings/custom.py @@ -1,13 +1,18 @@ +import datetime +import logging import os from pathlib import Path from typing import Any from celery.schedules import crontab +from dateparser.languages.loader import LocaleDataLoader from paperless.settings.parsers import get_choice_from_env from paperless.settings.parsers import get_int_from_env from paperless.settings.parsers import parse_dict_from_str +logger = logging.getLogger(__name__) + def parse_hosting_settings() -> tuple[str | None, str, str, str, str]: script_name = os.getenv("PAPERLESS_FORCE_SCRIPT_NAME") @@ -295,3 +300,48 @@ def parse_db_settings(data_dir: Path) -> dict[str, dict[str, Any]]: ) return {"default": db_config} + + +def parse_dateparser_languages(languages: str | None) -> list[str]: + language_list = languages.split("+") if languages else [] + # There is an unfixed issue in zh-Hant and zh-Hans locales in the dateparser lib. + # See: https://github.com/scrapinghub/dateparser/issues/875 + for index, language in enumerate(language_list): + if language.startswith("zh-") and "zh" not in language_list: + logger.warning( + f"Chinese locale detected: {language}. dateparser might fail to parse" + f' some dates with this locale, so Chinese ("zh") will be used as a fallback.', + ) + language_list.append("zh") + + return list(LocaleDataLoader().get_locale_map(locales=language_list)) + + +def parse_ignore_dates( + env_ignore: str, + date_order: str, +) -> set[datetime.date]: + """ + If the PAPERLESS_IGNORE_DATES environment variable is set, parse the + user provided string(s) into dates + + Args: + env_ignore (str): The value of the environment variable, comma separated dates + date_order (str): The format of the date strings. + + Returns: + set[datetime.date]: The set of parsed date objects + """ + import dateparser + + ignored_dates = set() + for s in env_ignore.split(","): + d = dateparser.parse( + s, + settings={ + "DATE_ORDER": date_order, + }, + ) + if d: + ignored_dates.add(d.date()) + return ignored_dates diff --git a/src/paperless/tests/settings/test_custom_parsers.py b/src/paperless/tests/settings/test_custom_parsers.py index 8e2c1b672..06299abb3 100644 --- a/src/paperless/tests/settings/test_custom_parsers.py +++ b/src/paperless/tests/settings/test_custom_parsers.py @@ -1,3 +1,4 @@ +import datetime import os from pathlib import Path from typing import Any @@ -7,8 +8,10 @@ from celery.schedules import crontab from pytest_mock import MockerFixture from paperless.settings.custom import parse_beat_schedule +from paperless.settings.custom import parse_dateparser_languages from paperless.settings.custom import parse_db_settings from paperless.settings.custom import parse_hosting_settings +from paperless.settings.custom import parse_ignore_dates from paperless.settings.custom import parse_redis_url @@ -58,6 +61,24 @@ class TestRedisSocketConversion: ("redis://myredishost:6379", "redis://myredishost:6379"), id="host_with_port_unchanged", ), + # Credentials in unix:// URL contain multiple colons (user:password@) + # Regression test for https://github.com/paperless-ngx/paperless-ngx/pull/12239 + pytest.param( + "unix://user:password@/run/redis/redis.sock", + ( + "redis+socket://user:password@/run/redis/redis.sock", + "unix://user:password@/run/redis/redis.sock", + ), + id="redis_py_style_socket_with_credentials", + ), + pytest.param( + "redis+socket://user:password@/run/redis/redis.sock", + ( + "redis+socket://user:password@/run/redis/redis.sock", + "unix://user:password@/run/redis/redis.sock", + ), + id="celery_style_socket_with_credentials", + ), ], ) def test_redis_socket_parsing( @@ -512,3 +533,85 @@ class TestParseDbSettings: settings = parse_db_settings(tmp_path) assert settings == expected_database_settings + + +class TestParseIgnoreDates: + """Tests the parsing of the PAPERLESS_IGNORE_DATES setting value.""" + + def test_no_ignore_dates_set(self) -> None: + """ + GIVEN: + - No ignore dates are set + THEN: + - No ignore dates are parsed + """ + assert parse_ignore_dates("", "YMD") == set() + + @pytest.mark.parametrize( + ("env_str", "date_format", "expected"), + [ + pytest.param( + "1985-05-01", + "YMD", + {datetime.date(1985, 5, 1)}, + id="single-ymd", + ), + pytest.param( + "1985-05-01,1991-12-05", + "YMD", + {datetime.date(1985, 5, 1), datetime.date(1991, 12, 5)}, + id="multiple-ymd", + ), + pytest.param( + "2010-12-13", + "YMD", + {datetime.date(2010, 12, 13)}, + id="single-ymd-2", + ), + pytest.param( + "11.01.10", + "DMY", + {datetime.date(2010, 1, 11)}, + id="single-dmy", + ), + pytest.param( + "11.01.2001,15-06-1996", + "DMY", + {datetime.date(2001, 1, 11), datetime.date(1996, 6, 15)}, + id="multiple-dmy", + ), + ], + ) + def test_ignore_dates_parsed( + self, + env_str: str, + date_format: str, + expected: set[datetime.date], + ) -> None: + """ + GIVEN: + - Ignore dates are set per certain inputs + THEN: + - All ignore dates are parsed + """ + assert parse_ignore_dates(env_str, date_format) == expected + + +@pytest.mark.parametrize( + ("languages", "expected"), + [ + ("de", ["de"]), + ("zh", ["zh"]), + ("fr+en", ["fr", "en"]), + # Locales must be supported + ("en-001+fr-CA", ["en-001", "fr-CA"]), + ("en-001+fr", ["en-001", "fr"]), + # Special case for Chinese: variants seem to miss some dates, + # so we always add "zh" as a fallback. + ("en+zh-Hans-HK", ["en", "zh-Hans-HK", "zh"]), + ("en+zh-Hans", ["en", "zh-Hans", "zh"]), + ("en+zh-Hans+zh-Hant", ["en", "zh-Hans", "zh-Hant", "zh"]), + ], +) +def test_parse_dateparser_languages(languages: str, expected: list[str]) -> None: + assert sorted(parse_dateparser_languages(languages)) == sorted(expected) diff --git a/src/paperless/tests/test_settings.py b/src/paperless/tests/test_settings.py index cc9ad2081..b0ae3c0c5 100644 --- a/src/paperless/tests/test_settings.py +++ b/src/paperless/tests/test_settings.py @@ -1,73 +1,11 @@ -import datetime import os from unittest import TestCase from unittest import mock -import pytest -from celery.schedules import crontab - -from paperless.settings import _parse_base_paths -from paperless.settings import _parse_beat_schedule -from paperless.settings import _parse_dateparser_languages -from paperless.settings import _parse_ignore_dates from paperless.settings import _parse_paperless_url -from paperless.settings import _parse_redis_url from paperless.settings import default_threads_per_worker -class TestIgnoreDateParsing(TestCase): - """ - Tests the parsing of the PAPERLESS_IGNORE_DATES setting value - """ - - def _parse_checker(self, test_cases) -> None: - """ - Helper function to check ignore date parsing - - Args: - test_cases (_type_): _description_ - """ - for env_str, date_format, expected_date_set in test_cases: - self.assertSetEqual( - _parse_ignore_dates(env_str, date_format), - expected_date_set, - ) - - def test_no_ignore_dates_set(self) -> None: - """ - GIVEN: - - No ignore dates are set - THEN: - - No ignore dates are parsed - """ - self.assertSetEqual(_parse_ignore_dates(""), set()) - - def test_single_ignore_dates_set(self) -> None: - """ - GIVEN: - - Ignore dates are set per certain inputs - THEN: - - All ignore dates are parsed - """ - test_cases = [ - ("1985-05-01", "YMD", {datetime.date(1985, 5, 1)}), - ( - "1985-05-01,1991-12-05", - "YMD", - {datetime.date(1985, 5, 1), datetime.date(1991, 12, 5)}, - ), - ("2010-12-13", "YMD", {datetime.date(2010, 12, 13)}), - ("11.01.10", "DMY", {datetime.date(2010, 1, 11)}), - ( - "11.01.2001,15-06-1996", - "DMY", - {datetime.date(2001, 1, 11), datetime.date(1996, 6, 15)}, - ), - ] - - self._parse_checker(test_cases) - - class TestThreadCalculation(TestCase): def test_workers_threads(self) -> None: """ @@ -94,289 +32,6 @@ class TestThreadCalculation(TestCase): self.assertLessEqual(default_workers * default_threads, i) -class TestRedisSocketConversion(TestCase): - def test_redis_socket_parsing(self) -> None: - """ - GIVEN: - - Various Redis connection URI formats - WHEN: - - The URI is parsed - THEN: - - Socket based URIs are translated - - Non-socket URIs are unchanged - - None provided uses default - """ - - for input, expected in [ - # Nothing is set - (None, ("redis://localhost:6379", "redis://localhost:6379")), - # celery style - ( - "redis+socket:///run/redis/redis.sock", - ( - "redis+socket:///run/redis/redis.sock", - "unix:///run/redis/redis.sock", - ), - ), - # redis-py / channels-redis style - ( - "unix:///run/redis/redis.sock", - ( - "redis+socket:///run/redis/redis.sock", - "unix:///run/redis/redis.sock", - ), - ), - # celery style with db - ( - "redis+socket:///run/redis/redis.sock?virtual_host=5", - ( - "redis+socket:///run/redis/redis.sock?virtual_host=5", - "unix:///run/redis/redis.sock?db=5", - ), - ), - # redis-py / channels-redis style with db - ( - "unix:///run/redis/redis.sock?db=10", - ( - "redis+socket:///run/redis/redis.sock?virtual_host=10", - "unix:///run/redis/redis.sock?db=10", - ), - ), - # Just a host with a port - ( - "redis://myredishost:6379", - ("redis://myredishost:6379", "redis://myredishost:6379"), - ), - ]: - result = _parse_redis_url(input) - self.assertTupleEqual(expected, result) - - -class TestCeleryScheduleParsing(TestCase): - MAIL_EXPIRE_TIME = 9.0 * 60.0 - CLASSIFIER_EXPIRE_TIME = 59.0 * 60.0 - INDEX_EXPIRE_TIME = 23.0 * 60.0 * 60.0 - SANITY_EXPIRE_TIME = ((7.0 * 24.0) - 1.0) * 60.0 * 60.0 - EMPTY_TRASH_EXPIRE_TIME = 23.0 * 60.0 * 60.0 - RUN_SCHEDULED_WORKFLOWS_EXPIRE_TIME = 59.0 * 60.0 - LLM_INDEX_EXPIRE_TIME = 23.0 * 60.0 * 60.0 - CLEANUP_EXPIRED_SHARE_BUNDLES_EXPIRE_TIME = 23.0 * 60.0 * 60.0 - - def test_schedule_configuration_default(self) -> None: - """ - GIVEN: - - No configured task schedules - WHEN: - - The celery beat schedule is built - THEN: - - The default schedule is returned - """ - schedule = _parse_beat_schedule() - - self.assertDictEqual( - { - "Check all e-mail accounts": { - "task": "paperless_mail.tasks.process_mail_accounts", - "schedule": crontab(minute="*/10"), - "options": {"expires": self.MAIL_EXPIRE_TIME}, - }, - "Train the classifier": { - "task": "documents.tasks.train_classifier", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.CLASSIFIER_EXPIRE_TIME}, - }, - "Optimize the index": { - "task": "documents.tasks.index_optimize", - "schedule": crontab(minute=0, hour=0), - "options": {"expires": self.INDEX_EXPIRE_TIME}, - }, - "Perform sanity check": { - "task": "documents.tasks.sanity_check", - "schedule": crontab(minute=30, hour=0, day_of_week="sun"), - "options": {"expires": self.SANITY_EXPIRE_TIME}, - }, - "Empty trash": { - "task": "documents.tasks.empty_trash", - "schedule": crontab(minute=0, hour="1"), - "options": {"expires": self.EMPTY_TRASH_EXPIRE_TIME}, - }, - "Check and run scheduled workflows": { - "task": "documents.tasks.check_scheduled_workflows", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.RUN_SCHEDULED_WORKFLOWS_EXPIRE_TIME}, - }, - "Rebuild LLM index": { - "task": "documents.tasks.llmindex_index", - "schedule": crontab(minute=10, hour=2), - "options": { - "expires": self.LLM_INDEX_EXPIRE_TIME, - }, - }, - "Cleanup expired share link bundles": { - "task": "documents.tasks.cleanup_expired_share_link_bundles", - "schedule": crontab(minute=0, hour=2), - "options": { - "expires": self.CLEANUP_EXPIRED_SHARE_BUNDLES_EXPIRE_TIME, - }, - }, - }, - schedule, - ) - - def test_schedule_configuration_changed(self) -> None: - """ - GIVEN: - - Email task is configured non-default - WHEN: - - The celery beat schedule is built - THEN: - - The email task is configured per environment - - The default schedule is returned for other tasks - """ - with mock.patch.dict( - os.environ, - {"PAPERLESS_EMAIL_TASK_CRON": "*/50 * * * mon"}, - ): - schedule = _parse_beat_schedule() - - self.assertDictEqual( - { - "Check all e-mail accounts": { - "task": "paperless_mail.tasks.process_mail_accounts", - "schedule": crontab(minute="*/50", day_of_week="mon"), - "options": {"expires": self.MAIL_EXPIRE_TIME}, - }, - "Train the classifier": { - "task": "documents.tasks.train_classifier", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.CLASSIFIER_EXPIRE_TIME}, - }, - "Optimize the index": { - "task": "documents.tasks.index_optimize", - "schedule": crontab(minute=0, hour=0), - "options": {"expires": self.INDEX_EXPIRE_TIME}, - }, - "Perform sanity check": { - "task": "documents.tasks.sanity_check", - "schedule": crontab(minute=30, hour=0, day_of_week="sun"), - "options": {"expires": self.SANITY_EXPIRE_TIME}, - }, - "Empty trash": { - "task": "documents.tasks.empty_trash", - "schedule": crontab(minute=0, hour="1"), - "options": {"expires": self.EMPTY_TRASH_EXPIRE_TIME}, - }, - "Check and run scheduled workflows": { - "task": "documents.tasks.check_scheduled_workflows", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.RUN_SCHEDULED_WORKFLOWS_EXPIRE_TIME}, - }, - "Rebuild LLM index": { - "task": "documents.tasks.llmindex_index", - "schedule": crontab(minute=10, hour=2), - "options": { - "expires": self.LLM_INDEX_EXPIRE_TIME, - }, - }, - "Cleanup expired share link bundles": { - "task": "documents.tasks.cleanup_expired_share_link_bundles", - "schedule": crontab(minute=0, hour=2), - "options": { - "expires": self.CLEANUP_EXPIRED_SHARE_BUNDLES_EXPIRE_TIME, - }, - }, - }, - schedule, - ) - - def test_schedule_configuration_disabled(self) -> None: - """ - GIVEN: - - Search index task is disabled - WHEN: - - The celery beat schedule is built - THEN: - - The search index task is not present - - The default schedule is returned for other tasks - """ - with mock.patch.dict(os.environ, {"PAPERLESS_INDEX_TASK_CRON": "disable"}): - schedule = _parse_beat_schedule() - - self.assertDictEqual( - { - "Check all e-mail accounts": { - "task": "paperless_mail.tasks.process_mail_accounts", - "schedule": crontab(minute="*/10"), - "options": {"expires": self.MAIL_EXPIRE_TIME}, - }, - "Train the classifier": { - "task": "documents.tasks.train_classifier", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.CLASSIFIER_EXPIRE_TIME}, - }, - "Perform sanity check": { - "task": "documents.tasks.sanity_check", - "schedule": crontab(minute=30, hour=0, day_of_week="sun"), - "options": {"expires": self.SANITY_EXPIRE_TIME}, - }, - "Empty trash": { - "task": "documents.tasks.empty_trash", - "schedule": crontab(minute=0, hour="1"), - "options": {"expires": self.EMPTY_TRASH_EXPIRE_TIME}, - }, - "Check and run scheduled workflows": { - "task": "documents.tasks.check_scheduled_workflows", - "schedule": crontab(minute="5", hour="*/1"), - "options": {"expires": self.RUN_SCHEDULED_WORKFLOWS_EXPIRE_TIME}, - }, - "Rebuild LLM index": { - "task": "documents.tasks.llmindex_index", - "schedule": crontab(minute=10, hour=2), - "options": { - "expires": self.LLM_INDEX_EXPIRE_TIME, - }, - }, - "Cleanup expired share link bundles": { - "task": "documents.tasks.cleanup_expired_share_link_bundles", - "schedule": crontab(minute=0, hour=2), - "options": { - "expires": self.CLEANUP_EXPIRED_SHARE_BUNDLES_EXPIRE_TIME, - }, - }, - }, - schedule, - ) - - def test_schedule_configuration_disabled_all(self) -> None: - """ - GIVEN: - - All tasks are disabled - WHEN: - - The celery beat schedule is built - THEN: - - No tasks are scheduled - """ - with mock.patch.dict( - os.environ, - { - "PAPERLESS_EMAIL_TASK_CRON": "disable", - "PAPERLESS_TRAIN_TASK_CRON": "disable", - "PAPERLESS_SANITY_TASK_CRON": "disable", - "PAPERLESS_INDEX_TASK_CRON": "disable", - "PAPERLESS_EMPTY_TRASH_TASK_CRON": "disable", - "PAPERLESS_WORKFLOW_SCHEDULED_TASK_CRON": "disable", - "PAPERLESS_LLM_INDEX_TASK_CRON": "disable", - "PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON": "disable", - }, - ): - schedule = _parse_beat_schedule() - - self.assertDictEqual( - {}, - schedule, - ) - - class TestPaperlessURLSettings(TestCase): def test_paperless_url(self) -> None: """ @@ -399,84 +54,3 @@ class TestPaperlessURLSettings(TestCase): self.assertIn(url, settings.CSRF_TRUSTED_ORIGINS) self.assertIn(url, settings.CORS_ALLOWED_ORIGINS) - - -class TestPathSettings(TestCase): - def test_default_paths(self) -> None: - """ - GIVEN: - - PAPERLESS_FORCE_SCRIPT_NAME is not set - WHEN: - - Settings are parsed - THEN: - - Paths are as expected - """ - base_paths = _parse_base_paths() - self.assertEqual(None, base_paths[0]) # FORCE_SCRIPT_NAME - self.assertEqual("/", base_paths[1]) # BASE_URL - self.assertEqual("/accounts/login/", base_paths[2]) # LOGIN_URL - self.assertEqual("/dashboard", base_paths[3]) # LOGIN_REDIRECT_URL - self.assertEqual( - "/accounts/login/?loggedout=1", - base_paths[4], - ) # LOGOUT_REDIRECT_URL - - @mock.patch("os.environ", {"PAPERLESS_FORCE_SCRIPT_NAME": "/paperless"}) - def test_subpath(self) -> None: - """ - GIVEN: - - PAPERLESS_FORCE_SCRIPT_NAME is set - WHEN: - - Settings are parsed - THEN: - - The path is returned and present in related settings - """ - base_paths = _parse_base_paths() - self.assertEqual("/paperless", base_paths[0]) # FORCE_SCRIPT_NAME - self.assertEqual("/paperless/", base_paths[1]) # BASE_URL - self.assertEqual("/paperless/accounts/login/", base_paths[2]) # LOGIN_URL - self.assertEqual("/paperless/dashboard", base_paths[3]) # LOGIN_REDIRECT_URL - self.assertEqual( - "/paperless/accounts/login/?loggedout=1", - base_paths[4], - ) # LOGOUT_REDIRECT_URL - - @mock.patch( - "os.environ", - { - "PAPERLESS_FORCE_SCRIPT_NAME": "/paperless", - "PAPERLESS_LOGOUT_REDIRECT_URL": "/foobar/", - }, - ) - def test_subpath_with_explicit_logout_url(self) -> None: - """ - GIVEN: - - PAPERLESS_FORCE_SCRIPT_NAME is set and so is PAPERLESS_LOGOUT_REDIRECT_URL - WHEN: - - Settings are parsed - THEN: - - The correct logout redirect URL is returned - """ - base_paths = _parse_base_paths() - self.assertEqual("/paperless/", base_paths[1]) # BASE_URL - self.assertEqual("/foobar/", base_paths[4]) # LOGOUT_REDIRECT_URL - - -@pytest.mark.parametrize( - ("languages", "expected"), - [ - ("de", ["de"]), - ("zh", ["zh"]), - ("fr+en", ["fr", "en"]), - # Locales must be supported - ("en-001+fr-CA", ["en-001", "fr-CA"]), - ("en-001+fr", ["en-001", "fr"]), - # Special case for Chinese: variants seem to miss some dates, - # so we always add "zh" as a fallback. - ("en+zh-Hans-HK", ["en", "zh-Hans-HK", "zh"]), - ("en+zh-Hans", ["en", "zh-Hans", "zh"]), - ("en+zh-Hans+zh-Hant", ["en", "zh-Hans", "zh-Hant", "zh"]), - ], -) -def test_parser_date_parser_languages(languages, expected) -> None: - assert sorted(_parse_dateparser_languages(languages)) == sorted(expected)