Backend (103 tests): - Unit tests for listing_service, export_service, district_service - Regression tests for API response contracts and query parameter validation - Integration tests for API workflows, Redis listing cache, listing processor pipeline, and repository advanced queries - E2E tests for streaming with filters, batching, caching, and task management Frontend (116 tests): - Service tests for apiClient, streamingService, taskService, listingService, healthService - Hook tests for useTaskProgress (WebSocket + polling) - Component tests for PropertyCard, FilterPanel, Header, ListView, TaskProgressDrawer, TaskIndicator, StreamingProgressBar, HealthIndicator - E2E tests for filter-stream-display flow Infrastructure: - Add pytest-xdist and test markers (regression, integration, e2e) - Add conftest fixtures: fake_redis, rent_listing_factory, seeded_repository - Add vitest + testing-library + MSW for frontend testing
122 lines
3.9 KiB
Python
122 lines
3.9 KiB
Python
"""Advanced integration tests for ListingRepository."""
|
|
import asyncio
|
|
|
|
import pytest
|
|
from sqlalchemy import Engine
|
|
|
|
from models.listing import FurnishType, ListingType, QueryParameters
|
|
from repositories.listing_repository import ListingRepository
|
|
|
|
|
|
# ---------- Count and basic queries ----------
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_count_matches_get_listings(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT)
|
|
count = seeded_repository.count_listings(qp)
|
|
listings = await seeded_repository.get_listings(query_parameters=qp)
|
|
assert count == len(listings)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stream_with_small_page_size(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT)
|
|
rows = list(seeded_repository.stream_listings_optimized(qp, page_size=3))
|
|
assert len(rows) == 10
|
|
|
|
|
|
# ---------- Filter tests ----------
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_filter_by_bedrooms(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT, min_bedrooms=2, max_bedrooms=2)
|
|
listings = await seeded_repository.get_listings(query_parameters=qp)
|
|
for listing in listings:
|
|
assert listing.number_of_bedrooms == 2
|
|
assert len(listings) > 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_filter_by_price_range(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT, min_price=1500, max_price=2500)
|
|
listings = await seeded_repository.get_listings(query_parameters=qp)
|
|
for listing in listings:
|
|
assert 1500 <= listing.price <= 2500
|
|
assert len(listings) > 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_filter_by_max_sqm(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT, max_sqm=50)
|
|
listings = await seeded_repository.get_listings(query_parameters=qp)
|
|
for listing in listings:
|
|
assert listing.square_meters is not None
|
|
assert listing.square_meters <= 50
|
|
assert len(listings) > 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_filter_by_furnish_type(
|
|
in_memory_engine: Engine,
|
|
rent_listing_factory,
|
|
) -> None:
|
|
repo = ListingRepository(engine=in_memory_engine)
|
|
furnished = rent_listing_factory(id=1, furnish_type=FurnishType.FURNISHED)
|
|
unfurnished = rent_listing_factory(id=2, furnish_type=FurnishType.UNFURNISHED)
|
|
await repo.upsert_listings([furnished, unfurnished])
|
|
|
|
qp = QueryParameters(
|
|
listing_type=ListingType.RENT,
|
|
furnish_types=[FurnishType.FURNISHED],
|
|
)
|
|
listings = await repo.get_listings(query_parameters=qp)
|
|
assert len(listings) == 1
|
|
assert listings[0].furnish_type == FurnishType.FURNISHED
|
|
|
|
|
|
# ---------- Concurrency ----------
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_concurrent_upserts(
|
|
in_memory_engine: Engine,
|
|
rent_listing_factory,
|
|
) -> None:
|
|
repo = ListingRepository(engine=in_memory_engine)
|
|
|
|
async def upsert_batch(start_id: int) -> None:
|
|
listings = [rent_listing_factory(id=start_id + i) for i in range(5)]
|
|
await repo.upsert_listings(listings)
|
|
|
|
await asyncio.gather(
|
|
upsert_batch(1000),
|
|
upsert_batch(2000),
|
|
upsert_batch(3000),
|
|
upsert_batch(4000),
|
|
upsert_batch(5000),
|
|
)
|
|
|
|
qp = QueryParameters(listing_type=ListingType.RENT)
|
|
total = repo.count_listings(qp)
|
|
assert total == 25
|
|
|
|
|
|
# ---------- Streaming ----------
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stream_optimized_returns_dicts(seeded_repository: ListingRepository) -> None:
|
|
qp = QueryParameters(listing_type=ListingType.RENT)
|
|
rows = list(seeded_repository.stream_listings_optimized(qp))
|
|
|
|
assert len(rows) > 0
|
|
for row in rows:
|
|
assert isinstance(row, dict)
|
|
assert "id" in row
|
|
assert "price" in row
|
|
assert "number_of_bedrooms" in row
|
|
assert "square_meters" in row
|
|
assert "longitude" in row
|
|
assert "latitude" in row
|