Add comprehensive test suite: 219 new tests across backend and frontend

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
This commit is contained in:
Viktor Barzin 2026-02-10 21:59:45 +00:00
parent a3ac9cc060
commit 8d22c97320
No known key found for this signature in database
GPG key ID: 0EB088298288D958
36 changed files with 5447 additions and 19 deletions

View file

@ -1,7 +1,8 @@
"""Shared pytest fixtures for the test suite."""
from datetime import datetime
from typing import AsyncGenerator, Generator
from typing import Any, AsyncGenerator, Callable, Generator
import pytest
import fakeredis
from sqlalchemy import Engine
from sqlmodel import SQLModel, Session, create_engine
from httpx import ASGITransport, AsyncClient
@ -184,3 +185,59 @@ async def async_client(
# Clean up dependency overrides
app.dependency_overrides.clear()
@pytest.fixture
def fake_redis() -> Generator[fakeredis.FakeRedis, None, None]:
"""Create a fakeredis client, flushed after each test."""
client = fakeredis.FakeRedis(decode_responses=True)
yield client
client.flushall()
@pytest.fixture
def rent_listing_factory() -> Callable[..., RentListing]:
"""Factory function that creates RentListing with overridable defaults."""
_counter = 0
def _create(**overrides: Any) -> RentListing:
nonlocal _counter
_counter += 1
defaults: dict[str, Any] = dict(
id=_counter,
price=2000.0,
number_of_bedrooms=2,
square_meters=55.0,
agency="Test Agency",
council_tax_band="C",
longitude=-0.1276,
latitude=51.5074,
price_history_json="[]",
listing_site=ListingSite.RIGHTMOVE,
last_seen=datetime.now(),
photo_thumbnail="https://example.com/photo.jpg",
floorplan_image_paths=[],
additional_info={"property": {"visible": True}},
routing_info_json=None,
furnish_type=FurnishType.FURNISHED,
available_from=datetime.now(),
)
defaults.update(overrides)
return RentListing(**defaults)
return _create
@pytest.fixture
async def seeded_repository(
in_memory_engine: Engine,
rent_listing_factory: Callable[..., RentListing],
) -> ListingRepository:
"""Repository with 10 pre-seeded listings (varied price/bedrooms/sqm)."""
repo = ListingRepository(engine=in_memory_engine)
listings = [
rent_listing_factory(id=100 + i, price=1000 + i * 300, number_of_bedrooms=(i % 4) + 1, square_meters=30 + i * 10)
for i in range(10)
]
await repo.upsert_listings(listings)
return repo