Add flight comparator web app with full scan pipeline

Full-stack flight price scanner built on fast-flights v3 (SOCS cookie bypass):

Backend (FastAPI + SQLite):
- REST API with rate limiting, Pydantic v2 validation, paginated responses
- Scan pipeline: resolves airports, queries every day in the window, saves
  individual flights + aggregate route stats to SQLite
- Background async scan processor with real-time progress tracking
- Airport search endpoint backed by OpenFlights dataset
- Daily scan window (all dates, not monthly samples)

Frontend (React 19 + TypeScript + Tailwind CSS v4):
- Dashboard with live scan status and recent scans
- Create scan form: country mode or specific airports (searchable dropdown)
- Scan detail page with expandable route rows showing individual flights
  (date, airline, departure, arrival, price) loaded on demand
- AirportSearch component with debounced live search and multi-select

Database:
- scans → routes → flights schema with FK cascade and auto-update triggers
- Migrations for schema evolution (relaxed country constraint)

Tests:
- 74 tests: unit + integration, isolated per-test SQLite DB
- Confirmed flight fixtures in tests/confirmed_flights.json (50 real flights,
  BDS→FMM Ryanair + BDS→DUS Eurowings, scraped Feb 2026)
- Integration tests parametrized from confirmed routes

Docker:
- Multi-stage builds, Compose orchestration, Nginx reverse proxy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 17:11:51 +01:00
parent aea7590874
commit 6421f83ca7
67 changed files with 37173 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
"""
Smoke tests for searcher module.
"""
import sys
sys.path.insert(0, '..')
from searcher import _parse_duration
def test_parse_duration():
"""Test duration parsing logic."""
assert _parse_duration("9h 30m") == 570
assert _parse_duration("9h") == 540
assert _parse_duration("90m") == 90
assert _parse_duration("10h 15m") == 615
assert _parse_duration("") == 0
print("✓ Duration parsing works")
def test_parse_duration_edge_cases():
"""Test edge cases in duration parsing."""
assert _parse_duration("0h 0m") == 0
assert _parse_duration("1h 1m") == 61
assert _parse_duration("24h") == 1440
print("✓ Duration parsing edge cases work")
if __name__ == "__main__":
test_parse_duration()
test_parse_duration_edge_cases()
print("\n✅ All searcher tests passed!")
print(" Note: Full API integration tests require fast-flights and live network")