mirror of
https://github.com/mblanke/Gov_Travel_App.git
synced 2026-03-01 14:10:22 -05:00
Integrate OpenFlights API for free, no-auth flight data generation
- Added openFlightsService.js to fetch and cache OpenFlights airport/airline/routes data - Validates airport codes exist in OpenFlights database (6072+ airports) - Generates realistic flights using major international airlines - Creates varied routing options: direct, 1-stop, 2-stop flights - Updated flightService.js to use OpenFlights as primary source before Amadeus - OpenFlights as fallback if Amadeus unavailable or returns no results - No API keys or authentication required - Cached locally to avoid repeated network requests - Realistic pricing, times, and stop locations Docker container rebuilt with OpenFlights integration.
This commit is contained in:
95
tests/test_scraper_coverage.py
Normal file
95
tests/test_scraper_coverage.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from gov_travel.scrapers import _extract_country_from_title, _normalize_header
|
||||
|
||||
DEFAULT_DB = Path(__file__).resolve().parent.parent / "data" / "travel_rates_scraped.sqlite3"
|
||||
DB_PATH = Path(os.environ.get("GOV_TRAVEL_DB", DEFAULT_DB))
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def conn():
|
||||
if not DB_PATH.exists():
|
||||
pytest.skip(
|
||||
f"Scraped DB not found at {DB_PATH}. Run `python -m gov_travel.main --db {DB_PATH}` first."
|
||||
)
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
yield connection
|
||||
connection.close()
|
||||
|
||||
|
||||
def _norm(value):
|
||||
return value.strip().lower() if isinstance(value, str) else value
|
||||
|
||||
|
||||
@pytest.mark.accommodations
|
||||
def test_accommodations_cover_all_raw_cities(conn):
|
||||
cursor = conn.execute("SELECT data_json FROM raw_tables WHERE source = 'accommodations'")
|
||||
expected_cities = set()
|
||||
for (data_json,) in cursor.fetchall():
|
||||
rows = json.loads(data_json)
|
||||
for row in rows:
|
||||
normalized = {_normalize_header(k): v for k, v in row.items()}
|
||||
city = normalized.get("city") or normalized.get("location")
|
||||
if city:
|
||||
expected_cities.add(_norm(city))
|
||||
|
||||
actual_cities = {
|
||||
_norm(city)
|
||||
for (city,) in conn.execute(
|
||||
"SELECT DISTINCT city FROM accommodations WHERE city IS NOT NULL"
|
||||
)
|
||||
}
|
||||
|
||||
missing = sorted(expected_cities - actual_cities)
|
||||
assert not missing, f"Missing accommodations entries for {len(missing)} cities (e.g., {missing[:5]})"
|
||||
|
||||
|
||||
@pytest.mark.per_diem
|
||||
def test_per_diem_covers_all_countries(conn):
|
||||
cursor = conn.execute(
|
||||
"SELECT title FROM raw_tables WHERE source = 'international' AND title IS NOT NULL"
|
||||
)
|
||||
expected_countries = set()
|
||||
for (title,) in cursor.fetchall():
|
||||
country = _extract_country_from_title(title)
|
||||
if country:
|
||||
expected_countries.add(_norm(country))
|
||||
|
||||
actual_countries = {
|
||||
_norm(country)
|
||||
for (country,) in conn.execute(
|
||||
"SELECT DISTINCT country FROM rate_entries WHERE source = 'international' AND country IS NOT NULL"
|
||||
)
|
||||
}
|
||||
|
||||
missing = sorted(expected_countries - actual_countries)
|
||||
assert not missing, f"Countries missing per-diem entries: {missing}"
|
||||
|
||||
|
||||
@pytest.mark.per_diem
|
||||
def test_per_diem_has_meal_types(conn):
|
||||
meal_types = {"breakfast", "lunch", "dinner", "incidental amount"}
|
||||
cursor = conn.execute(
|
||||
"SELECT DISTINCT country FROM rate_entries WHERE source = 'international' AND country IS NOT NULL"
|
||||
)
|
||||
|
||||
missing_countries = []
|
||||
for (country,) in cursor.fetchall():
|
||||
rate_types = {
|
||||
_norm(rate_type)
|
||||
for (rate_type,) in conn.execute(
|
||||
"SELECT DISTINCT rate_type FROM rate_entries WHERE source = 'international' AND country = ?",
|
||||
(country,),
|
||||
)
|
||||
if rate_type
|
||||
}
|
||||
if not rate_types.intersection(meal_types):
|
||||
missing_countries.append(country)
|
||||
|
||||
assert not missing_countries, f"Countries without meal-type entries: {missing_countries}"
|
||||
Reference in New Issue
Block a user