"""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 == []