133 lines
3.3 KiB
Python
133 lines
3.3 KiB
Python
|
|
"""Integration tests for Redis-based listing cache."""
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from models.listing import ListingType, QueryParameters
|
||
|
|
from services.listing_cache import (
|
||
|
|
begin_cache_population,
|
||
|
|
cache_features_batch,
|
||
|
|
cache_features_batch_staged,
|
||
|
|
delete_staging_key,
|
||
|
|
finalize_cache_population,
|
||
|
|
get_cached_count,
|
||
|
|
get_cached_features,
|
||
|
|
invalidate_cache,
|
||
|
|
make_cache_key,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(autouse=True)
|
||
|
|
def patch_redis(fake_redis, monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
"""Route all cache operations through fakeredis."""
|
||
|
|
monkeypatch.setattr("services.listing_cache._get_redis_client", lambda: fake_redis)
|
||
|
|
|
||
|
|
|
||
|
|
def _make_qp(**kwargs) -> QueryParameters:
|
||
|
|
return QueryParameters(listing_type=ListingType.RENT, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def _sample_features(n: int) -> list[dict]:
|
||
|
|
return [{"type": "Feature", "id": i, "properties": {"price": 1000 + i}} for i in range(n)]
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Basic read/write ----------
|
||
|
|
|
||
|
|
|
||
|
|
def test_cache_miss_returns_none() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
assert get_cached_count(qp) is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_cache_write_then_read() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
features = _sample_features(5)
|
||
|
|
cache_features_batch(qp, features)
|
||
|
|
|
||
|
|
count = get_cached_count(qp)
|
||
|
|
assert count == 5
|
||
|
|
|
||
|
|
|
||
|
|
def test_batch_retrieval() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
cache_features_batch(qp, _sample_features(10))
|
||
|
|
|
||
|
|
batches = list(get_cached_features(qp, batch_size=3))
|
||
|
|
sizes = [len(b) for b in batches]
|
||
|
|
assert sizes == [3, 3, 3, 1]
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Cache key behaviour ----------
|
||
|
|
|
||
|
|
|
||
|
|
def test_cache_key_deterministic() -> None:
|
||
|
|
qp1 = _make_qp()
|
||
|
|
qp2 = _make_qp()
|
||
|
|
assert make_cache_key(qp1) == make_cache_key(qp2)
|
||
|
|
|
||
|
|
|
||
|
|
def test_cache_key_different_for_different_params() -> None:
|
||
|
|
rent = _make_qp()
|
||
|
|
buy = QueryParameters(listing_type=ListingType.BUY)
|
||
|
|
assert make_cache_key(rent) != make_cache_key(buy)
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Staged population ----------
|
||
|
|
|
||
|
|
|
||
|
|
def test_staged_population_begin() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
staging_key = begin_cache_population(qp)
|
||
|
|
assert isinstance(staging_key, str)
|
||
|
|
assert "staging" in staging_key
|
||
|
|
|
||
|
|
|
||
|
|
def test_staged_write_then_finalize() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
staging_key = begin_cache_population(qp)
|
||
|
|
cache_features_batch_staged(staging_key, _sample_features(4))
|
||
|
|
finalize_cache_population(staging_key, qp)
|
||
|
|
|
||
|
|
assert get_cached_count(qp) == 4
|
||
|
|
|
||
|
|
|
||
|
|
def test_staging_key_deleted_on_cleanup(fake_redis) -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
staging_key = begin_cache_population(qp)
|
||
|
|
cache_features_batch_staged(staging_key, _sample_features(2))
|
||
|
|
delete_staging_key(staging_key)
|
||
|
|
|
||
|
|
assert fake_redis.exists(staging_key) == 0
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Invalidation ----------
|
||
|
|
|
||
|
|
|
||
|
|
def test_invalidation() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
cache_features_batch(qp, _sample_features(5))
|
||
|
|
assert get_cached_count(qp) == 5
|
||
|
|
|
||
|
|
invalidate_cache()
|
||
|
|
assert get_cached_count(qp) is None
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Edge cases ----------
|
||
|
|
|
||
|
|
|
||
|
|
def test_empty_features_batch_noop() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
cache_features_batch(qp, [])
|
||
|
|
assert get_cached_count(qp) is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_multiple_batches_accumulate() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
cache_features_batch(qp, _sample_features(3))
|
||
|
|
cache_features_batch(qp, _sample_features(4))
|
||
|
|
assert get_cached_count(qp) == 7
|
||
|
|
|
||
|
|
|
||
|
|
def test_get_cached_features_empty() -> None:
|
||
|
|
qp = _make_qp()
|
||
|
|
batches = list(get_cached_features(qp))
|
||
|
|
assert batches == []
|