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
87 lines
4.2 KiB
Python
87 lines
4.2 KiB
Python
"""Unit tests for services/export_service.py."""
|
|
|
|
import pytest
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from models.listing import QueryParameters, ListingType
|
|
from services import export_service
|
|
|
|
|
|
class TestExportToCsv:
|
|
async def test_csv_export_calls_exporter(self, listing_repository, tmp_path):
|
|
output_path = tmp_path / "output.csv"
|
|
with patch("csv_exporter.export_to_csv", new_callable=AsyncMock) as mock_csv:
|
|
mock_csv.return_value = None
|
|
result = await export_service.export_to_csv(listing_repository, output_path)
|
|
mock_csv.assert_called_once()
|
|
assert result.success
|
|
|
|
async def test_csv_export_returns_correct_record_count(self, listing_repository, sample_rent_listings, tmp_path):
|
|
await listing_repository.upsert_listings(sample_rent_listings)
|
|
output_path = tmp_path / "output.csv"
|
|
with patch("csv_exporter.export_to_csv", new_callable=AsyncMock) as mock_csv:
|
|
mock_csv.return_value = None
|
|
result = await export_service.export_to_csv(listing_repository, output_path)
|
|
assert result.record_count == len(sample_rent_listings)
|
|
|
|
async def test_csv_export_passes_query_parameters(self, listing_repository, tmp_path):
|
|
output_path = tmp_path / "output.csv"
|
|
params = QueryParameters(listing_type=ListingType.RENT, min_bedrooms=2)
|
|
with patch("csv_exporter.export_to_csv", new_callable=AsyncMock) as mock_csv:
|
|
mock_csv.return_value = None
|
|
result = await export_service.export_to_csv(
|
|
listing_repository, output_path, query_parameters=params
|
|
)
|
|
assert result.success
|
|
assert str(output_path) in result.output_path
|
|
|
|
|
|
class TestExportToGeojson:
|
|
async def test_geojson_in_memory_returns_data(self, listing_repository):
|
|
fake_geojson = {"type": "FeatureCollection", "features": [{"type": "Feature"}]}
|
|
with patch("ui_exporter.export_immoweb", new_callable=AsyncMock) as mock_export:
|
|
mock_export.return_value = fake_geojson
|
|
result = await export_service.export_to_geojson(listing_repository)
|
|
assert result.data is not None
|
|
assert result.data["type"] == "FeatureCollection"
|
|
|
|
async def test_geojson_file_export_returns_path(self, listing_repository, tmp_path):
|
|
output_path = tmp_path / "output.geojson"
|
|
fake_geojson = {"type": "FeatureCollection", "features": []}
|
|
with patch("ui_exporter.export_immoweb", new_callable=AsyncMock) as mock_export:
|
|
mock_export.return_value = fake_geojson
|
|
result = await export_service.export_to_geojson(
|
|
listing_repository, output_path=output_path
|
|
)
|
|
assert result.output_path is not None
|
|
assert result.data is None
|
|
|
|
async def test_geojson_with_filters(self, listing_repository):
|
|
params = QueryParameters(listing_type=ListingType.RENT, min_bedrooms=2)
|
|
fake_geojson = {"type": "FeatureCollection", "features": []}
|
|
with patch("ui_exporter.export_immoweb", new_callable=AsyncMock) as mock_export:
|
|
mock_export.return_value = fake_geojson
|
|
result = await export_service.export_to_geojson(
|
|
listing_repository, query_parameters=params
|
|
)
|
|
assert result.success
|
|
mock_export.assert_called_once()
|
|
|
|
async def test_geojson_with_limit(self, listing_repository):
|
|
fake_geojson = {"type": "FeatureCollection", "features": []}
|
|
with patch("ui_exporter.export_immoweb", new_callable=AsyncMock) as mock_export:
|
|
mock_export.return_value = fake_geojson
|
|
result = await export_service.export_to_geojson(
|
|
listing_repository, limit=5
|
|
)
|
|
assert result.success
|
|
_, kwargs = mock_export.call_args
|
|
assert kwargs.get("limit") == 5
|
|
|
|
async def test_geojson_empty_data(self, listing_repository):
|
|
fake_geojson = {"type": "FeatureCollection", "features": []}
|
|
with patch("ui_exporter.export_immoweb", new_callable=AsyncMock) as mock_export:
|
|
mock_export.return_value = fake_geojson
|
|
result = await export_service.export_to_geojson(listing_repository)
|
|
assert result.record_count == 0
|