mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-03-10 11:11:23 +00:00
157 lines
5.3 KiB
Python
157 lines
5.3 KiB
Python
import os
|
|
import time
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from cachalot.settings import cachalot_settings
|
|
from django.conf import settings
|
|
from django.db import connection
|
|
from django.test import override_settings
|
|
from django.test.utils import CaptureQueriesContext
|
|
|
|
from documents.models import Tag
|
|
from paperless.db_cache import invalidate_db_cache
|
|
from paperless.settings import _parse_cachalot_settings
|
|
from paperless.settings import _parse_caches
|
|
|
|
|
|
def test_all_redis_caches_have_same_custom_prefix(monkeypatch) -> None:
|
|
"""
|
|
Check that when setting a custom Redis prefix,
|
|
it is set for both the Django default cache and the read cache.
|
|
"""
|
|
from paperless import settings
|
|
|
|
monkeypatch.setattr(settings, "_REDIS_KEY_PREFIX", "test_a_custom_key_prefix")
|
|
caches = _parse_caches()
|
|
assert caches["read-cache"]["KEY_PREFIX"] == "test_a_custom_key_prefix"
|
|
assert caches["default"]["KEY_PREFIX"] == "test_a_custom_key_prefix"
|
|
|
|
|
|
class TestDbCacheSettings:
|
|
def test_cachalot_default_settings(self) -> None:
|
|
# Cachalot must be installed even if disabled,
|
|
# so the cache can be invalidated anytime
|
|
assert "cachalot" not in settings.INSTALLED_APPS
|
|
cachalot_settings = _parse_cachalot_settings()
|
|
caches = _parse_caches()
|
|
|
|
# Default settings
|
|
assert not cachalot_settings["CACHALOT_ENABLED"]
|
|
assert cachalot_settings["CACHALOT_TIMEOUT"] == 3600
|
|
assert caches["read-cache"]["KEY_PREFIX"] == ""
|
|
assert caches["read-cache"]["LOCATION"] == "redis://localhost:6379"
|
|
|
|
# Fixed settings
|
|
assert cachalot_settings["CACHALOT_CACHE"] == "read-cache"
|
|
assert (
|
|
cachalot_settings["CACHALOT_QUERY_KEYGEN"]
|
|
== "paperless.db_cache.custom_get_query_cache_key"
|
|
)
|
|
assert (
|
|
cachalot_settings["CACHALOT_TABLE_KEYGEN"]
|
|
== "paperless.db_cache.custom_get_table_cache_key"
|
|
)
|
|
assert cachalot_settings["CACHALOT_FINAL_SQL_CHECK"] is True
|
|
|
|
@patch.dict(
|
|
os.environ,
|
|
{
|
|
"PAPERLESS_DB_READ_CACHE_ENABLED": "true",
|
|
"PAPERLESS_READ_CACHE_REDIS_URL": "redis://localhost:6380/7",
|
|
"PAPERLESS_READ_CACHE_TTL": "7200",
|
|
},
|
|
)
|
|
def test_cachalot_custom_settings(self) -> None:
|
|
settings = _parse_cachalot_settings()
|
|
|
|
assert settings["CACHALOT_ENABLED"]
|
|
assert settings["CACHALOT_TIMEOUT"] == 7200
|
|
assert settings["CACHALOT_CACHE"] == "read-cache"
|
|
assert (
|
|
settings["CACHALOT_QUERY_KEYGEN"]
|
|
== "paperless.db_cache.custom_get_query_cache_key"
|
|
)
|
|
assert (
|
|
settings["CACHALOT_TABLE_KEYGEN"]
|
|
== "paperless.db_cache.custom_get_table_cache_key"
|
|
)
|
|
assert settings["CACHALOT_FINAL_SQL_CHECK"] is True
|
|
|
|
@pytest.mark.parametrize(
|
|
("env_var_ttl", "expected_cachalot_timeout"),
|
|
[
|
|
# 0 or less will be ignored, and the default TTL will be set
|
|
("0", 3600),
|
|
("-1", 3600),
|
|
("-500000", 3600),
|
|
# Any positive value will be set, for a maximum of one year
|
|
("1", 1),
|
|
("7524", 7524),
|
|
("99999999999999", 31536000),
|
|
],
|
|
)
|
|
def test_cachalot_ttl_parsing(
|
|
self,
|
|
env_var_ttl: int,
|
|
expected_cachalot_timeout: int,
|
|
) -> None:
|
|
with patch.dict(os.environ, {"PAPERLESS_READ_CACHE_TTL": f"{env_var_ttl}"}):
|
|
cachalot_timeout = _parse_cachalot_settings()["CACHALOT_TIMEOUT"]
|
|
assert cachalot_timeout == expected_cachalot_timeout
|
|
|
|
|
|
@override_settings(
|
|
CACHALOT_ENABLED=True,
|
|
CACHALOT_TIMEOUT=1,
|
|
)
|
|
@pytest.mark.django_db(transaction=True)
|
|
def test_cache_hit_when_enabled() -> None:
|
|
cachalot_settings.reload()
|
|
|
|
assert cachalot_settings.CACHALOT_ENABLED
|
|
assert cachalot_settings.CACHALOT_TIMEOUT == 1
|
|
assert settings.CACHALOT_TIMEOUT == 1
|
|
|
|
# Read a table to populate the cache
|
|
list(list(Tag.objects.values_list("id", flat=True)))
|
|
|
|
# Invalidate the cache then read the database, there should be DB hit
|
|
invalidate_db_cache()
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
list(list(Tag.objects.values_list("id", flat=True)))
|
|
assert len(ctx)
|
|
|
|
# Doing the same request again should hit the cache, not the DB
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
list(list(Tag.objects.values_list("id", flat=True)))
|
|
assert not len(ctx)
|
|
|
|
# Wait the end of TTL
|
|
# Redis expire accuracy should be between 0 and 1 ms
|
|
time.sleep(1.002)
|
|
|
|
# Read the DB again. The DB should be hit because the cache has expired
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
list(list(Tag.objects.values_list("id", flat=True)))
|
|
assert len(ctx)
|
|
|
|
# Invalidate the cache at the end of test
|
|
invalidate_db_cache()
|
|
|
|
|
|
@pytest.mark.django_db(transaction=True)
|
|
def test_cache_is_disabled_by_default() -> None:
|
|
cachalot_settings.reload()
|
|
# Invalidate the cache just in case
|
|
invalidate_db_cache()
|
|
|
|
# Read the table multiple times: the DB should always be hit without cache
|
|
for _ in range(3):
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
list(list(Tag.objects.values_list("id", flat=True)))
|
|
assert len(ctx)
|
|
|
|
# Invalidate the cache at the end of test
|
|
invalidate_db_cache()
|