Files
Marc 'risson' Schmitt 5839b40efa packages/django-postgres-cache: avoid regex queries when listing keys if possible (#23160)
Co-authored-by: Ryan Pesek <rpesek@cloudflare.com>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2026-06-17 12:58:02 +00:00

69 lines
3.0 KiB
Python

"""Tests for ``DatabaseCache.keys`` glob-to-SQL translation.
``cache.keys("prefix*")`` must use ``__startswith`` (LIKE), not the regex
path. Pure unit tests — ``CacheEntry.objects`` is mocked.
"""
from typing import Any, cast
from unittest import TestCase, mock
from django_postgres_cache.backend import DatabaseCache
def _make_cache() -> DatabaseCache:
"""Construct a ``DatabaseCache`` without Django settings or a real DB."""
cache = DatabaseCache.__new__(DatabaseCache)
cache.key_prefix = ""
cache.version = 1
cache.key_func = lambda key, key_prefix, version: f":{version}:{key}"
# Reverse-key is not invoked in these tests (filter mocks return []), so
# an identity function is enough.
cache.reverse_key_func = lambda k: k
return cache
class TestKeysGlobToLookup(TestCase):
"""The SQL lookup chosen for each glob pattern shape."""
def _captured_filter_kwargs(self, pattern: str) -> dict[str, Any]:
"""Run ``cache.keys(pattern)`` and return the kwargs passed to
``CacheEntry.objects.filter(...)``."""
cache = _make_cache()
with mock.patch("django_postgres_cache.backend.CacheEntry") as mock_entry:
mock_entry.objects.filter.return_value.values_list.return_value = []
cache.keys(pattern)
self.assertEqual(mock_entry.objects.filter.call_count, 1)
return cast(dict[str, Any], mock_entry.objects.filter.call_args.kwargs)
def test_simple_prefix_glob_uses_startswith(self) -> None:
"""``cache.keys("foo*")`` uses ``__startswith``, not ``__regex``."""
kwargs = self._captured_filter_kwargs("foo*")
self.assertIn("cache_key__startswith", kwargs)
self.assertNotIn("cache_key__regex", kwargs)
self.assertNotIn("cache_key", kwargs)
def test_realistic_authentik_prefix_glob_uses_startswith(self) -> None:
"""The actual hot-query pattern from the bug report uses ``__startswith``."""
kwargs = self._captured_filter_kwargs("goauthentik.io/policies/app_access/*")
self.assertIn("cache_key__startswith", kwargs)
self.assertNotIn("cache_key__regex", kwargs)
def test_exact_match_uses_equality(self) -> None:
"""No-wildcard patterns use primary-key equality."""
kwargs = self._captured_filter_kwargs("exact")
self.assertIn("cache_key", kwargs)
self.assertNotIn("cache_key__startswith", kwargs)
self.assertNotIn("cache_key__regex", kwargs)
def test_complex_glob_falls_back_to_regex(self) -> None:
"""Multiple wildcards or non-suffix wildcards fall back to ``__regex``."""
kwargs = self._captured_filter_kwargs("foo*bar*")
self.assertIn("cache_key__regex", kwargs)
self.assertNotIn("cache_key__startswith", kwargs)
def test_leading_wildcard_falls_back_to_regex(self) -> None:
"""A leading wildcard cannot reduce to LIKE — fall back to ``__regex``."""
kwargs = self._captured_filter_kwargs("*foo")
self.assertIn("cache_key__regex", kwargs)
self.assertNotIn("cache_key__startswith", kwargs)