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

@ -0,0 +1,77 @@
import { http, HttpResponse } from 'msw';
export const handlers = [
// Health check
http.get('/api/status', () => {
return HttpResponse.json({ status: 'OK' });
}),
// Get listings
http.get('/api/listing', () => {
return HttpResponse.json({ listings: [] });
}),
// Get listing GeoJSON
http.get('/api/listing_geojson', () => {
return HttpResponse.json({
type: 'FeatureCollection',
features: [],
});
}),
// Stream listing GeoJSON
http.get('/api/listing_geojson/stream', () => {
const lines = [
JSON.stringify({ type: 'metadata', batch_size: 50, total_expected: 0, cached: false }),
JSON.stringify({ type: 'complete', total: 0 }),
].join('\n') + '\n';
return new HttpResponse(lines, {
headers: { 'Content-Type': 'application/x-ndjson' },
});
}),
// Refresh listings
http.post('/api/refresh_listings', () => {
return HttpResponse.json({ task_id: 'test-task-123', message: 'Task started' });
}),
// Task status
http.get('/api/task_status', () => {
return HttpResponse.json({
task_id: 'test-task-123',
status: 'PENDING',
result: null,
progress: null,
processed: null,
total: null,
message: null,
error: null,
traceback: null,
});
}),
// Tasks for user
http.get('/api/tasks_for_user', () => {
return HttpResponse.json([]);
}),
// Cancel task
http.post('/api/cancel_task', () => {
return HttpResponse.json({ success: true, message: 'Task cancelled' });
}),
// Clear all tasks
http.post('/api/clear_all_tasks', () => {
return HttpResponse.json({ success: true, count: 0, message: 'Cleared 0 tasks' });
}),
// Districts
http.get('/api/get_districts', () => {
return HttpResponse.json({
London: 'REGION^87490',
Westminster: 'REGION^93980',
Camden: 'REGION^93941',
});
}),
];

View file

@ -0,0 +1,4 @@
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);