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>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""
|
|
Smoke tests for airports module.
|
|
"""
|
|
|
|
import sys
|
|
sys.path.insert(0, '..')
|
|
|
|
from airports import get_airports_for_country, resolve_airport_list
|
|
|
|
|
|
def test_get_airports_for_country():
|
|
"""Test loading airports for a country."""
|
|
de_airports = get_airports_for_country("DE")
|
|
assert len(de_airports) > 0
|
|
assert all('iata' in a for a in de_airports)
|
|
assert all('name' in a for a in de_airports)
|
|
assert all('city' in a for a in de_airports)
|
|
print(f"✓ Found {len(de_airports)} airports in Germany")
|
|
|
|
|
|
def test_resolve_airport_list_from_country():
|
|
"""Test resolving airport list from country."""
|
|
airports = resolve_airport_list("DE", None)
|
|
assert len(airports) > 0
|
|
print(f"✓ Resolved {len(airports)} airports from country DE")
|
|
|
|
|
|
def test_resolve_airport_list_from_custom():
|
|
"""Test resolving airport list from custom --from argument."""
|
|
airports = resolve_airport_list(None, "FRA,MUC,BER")
|
|
assert len(airports) == 3
|
|
assert airports[0]['iata'] == 'FRA'
|
|
assert airports[1]['iata'] == 'MUC'
|
|
assert airports[2]['iata'] == 'BER'
|
|
print(f"✓ Resolved custom airport list: {[a['iata'] for a in airports]}")
|
|
|
|
|
|
def test_invalid_country():
|
|
"""Test handling of invalid country code."""
|
|
try:
|
|
get_airports_for_country("XX")
|
|
assert False, "Should have raised ValueError"
|
|
except ValueError as e:
|
|
assert "not found" in str(e)
|
|
print("✓ Invalid country code raises appropriate error")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_get_airports_for_country()
|
|
test_resolve_airport_list_from_country()
|
|
test_resolve_airport_list_from_custom()
|
|
test_invalid_country()
|
|
print("\n✅ All airports tests passed!")
|