Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions frontend/src/pages/ResultsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ export function ResultsPage() {
const [error, setError] = useState<string | null>(null)

const trackIntervalOptions = useMemo(() => [
{ value: 1, label: t('common.checkEveryMin', { n: 1 }) },
{ value: 5, label: t('common.checkEveryMin', { n: 5 }) },
{ value: 10, label: t('common.checkEveryMin', { n: 10 }) },
{ value: 30, label: t('common.checkEveryMin', { n: 30 }) },
{ value: 60, label: t('common.checkEveryHour', { n: 1 }) },
Expand Down
26 changes: 11 additions & 15 deletions tests/test_notifications_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_build_price_change_summary_format() -> None:
old_price=10.0,
new_price=12.5,
)
assert summary == "Trip - Madrid -> Zaragoza, 09:30, changed price from 10.00 to 12.50"
assert summary == "Price changed: Madrid Zaragoza, dep. 09:30. Price: €10.00 → €12.50"


def test_build_price_change_summary_old_price_unknown() -> None:
Expand All @@ -29,7 +29,7 @@ def test_build_price_change_summary_old_price_unknown() -> None:
old_price=None,
new_price=12.5,
)
assert summary == "Trip - Madrid -> Zaragoza, N/A, changed price from N/A to 12.50"
assert summary == "Price changed: Madrid Zaragoza, dep. N/A. Price: N/A → €12.50"


def test_notifications_email_crud(client: TestClient) -> None:
Expand Down Expand Up @@ -93,11 +93,11 @@ def test_notifications_browser_crud(client: TestClient) -> None:


def test_notifications_home_assistant_crud_and_masking(client: TestClient) -> None:
# HA URL and token are configured via env vars, not stored in DB.
# Only ha_notify_service is user-facing and stored.
payload = {
"type": "home_assistant",
"label": "HA notifier",
"ha_url": "http://localhost:8123",
"ha_token": "super-secret-token",
"ha_notify_service": "mobile_app_javier",
}

Expand All @@ -111,9 +111,8 @@ def test_notifications_home_assistant_crud_and_masking(client: TestClient) -> No

notif = next(n for n in notifications if n["id"] == notification_id)
assert notif["type"] == "home_assistant"
assert notif["ha_url"] == "http://localhost:8123"
assert notif["notify_service"] == "mobile_app_javier"
assert notif["has_ha_token"] is True
assert notif["ha_notify_service"] == "mobile_app_javier"
assert "ha_url" not in notif
assert "ha_token" not in notif

removed = client.delete(f"/api/notifications/{notification_id}")
Expand All @@ -122,16 +121,12 @@ def test_notifications_home_assistant_crud_and_masking(client: TestClient) -> No


def test_notifications_email_public_list_returns_masked_secret_indicators(client: TestClient) -> None:
# SMTP credentials are configured via env vars, not stored in DB.
# Only user-facing fields (email_to, email_subject) are stored and returned.
payload = {
"type": "email",
"label": "Masked email",
"smtp_host": "smtp.example.com",
"smtp_port": 587,
"smtp_username": "user@example.com",
"smtp_password": "super-secret-password",
"smtp_use_starttls": True,
"email_to": "to@example.com",
"email_from": "from@example.com",
"email_subject": "Price change alert",
}

Expand All @@ -144,9 +139,10 @@ def test_notifications_email_public_list_returns_masked_secret_indicators(client
notifications = listed.json().get("notifications", [])
notif = next(n for n in notifications if n["id"] == notification_id)

assert notif["smtp_username"] == "******"
assert notif["has_smtp_password"] is True
assert notif["email_to"] == "to@example.com"
assert notif["email_subject"] == "Price change alert"
assert "smtp_password" not in notif
assert "smtp_username" not in notif


def test_notifications_init_db_migrates_legacy_notification_columns(tmp_path: Path) -> None:
Expand Down
29 changes: 17 additions & 12 deletions tests/test_search_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@


def test_search_options_includes_zaragoza_calatayud_tafalla(client: TestClient):
"""GET /api/search/options must include Zaragoza, Calatayud, Tafalla."""
"""GET /api/search/options must include Zaragoza-Delicias, Calatayud, Tafalla."""
r = client.get("/api/search/options")
assert r.status_code == 200
data = r.json()
origins = data.get("origins", [])
destinations = data.get("destinations", [])
for name in ("Zaragoza", "Calatayud", "Tafalla"):
# Zaragoza has several stations (Delicias, Goya, etc.) but no plain "Zaragoza"
assert any("Zaragoza" in name for name in origins), "Missing any Zaragoza station in origins"
assert any("Zaragoza" in name for name in destinations), "Missing any Zaragoza station in destinations"
for name in ("Calatayud", "Tafalla"):
assert name in origins, f"Missing origin: {name}"
assert name in destinations, f"Missing destination: {name}"

Expand All @@ -24,11 +27,12 @@ def test_search_returns_trains_when_backend_mocked(client: TestClient):
{"name": "AVE 0101", "price": 45.50, "duration_minutes": 90, "estimated_price_min": None, "estimated_price_max": None},
]
with patch("app.api.search._is_mock_enabled", return_value=False):
with patch("app.api.search._trains_from_gtfs_backend", return_value=mock_trains):
r = client.post(
"/api/search",
json={"date": "2025-06-15", "origin": "Madrid", "destination": "Zaragoza"},
)
with patch("app.api.search._is_possible_trains_enabled", return_value=False):
with patch("app.api.search._trains_from_gtfs_backend", return_value=mock_trains):
r = client.post(
"/api/search",
json={"date": "2025-06-15", "origin": "Madrid", "destination": "Zaragoza"},
)
assert r.status_code == 200
data = r.json()
assert "trains" in data
Expand All @@ -40,11 +44,12 @@ def test_search_returns_trains_when_backend_mocked(client: TestClient):
def test_search_returns_503_when_backend_fails(client: TestClient):
"""POST /api/search returns 503 when GTFS backend raises an exception."""
with patch("app.api.search._is_mock_enabled", return_value=False):
with patch("app.api.search._trains_from_gtfs_backend", side_effect=Exception("No se pudo conectar con Renfe")):
r = client.post(
"/api/search",
json={"date": "2025-06-15", "origin": "Madrid", "destination": "Barcelona"},
)
with patch("app.api.search._is_possible_trains_enabled", return_value=False):
with patch("app.api.search._trains_from_gtfs_backend", side_effect=Exception("No se pudo conectar con Renfe")):
r = client.post(
"/api/search",
json={"date": "2025-06-15", "origin": "Madrid", "destination": "Barcelona"},
)
assert r.status_code == 503
assert "detail" in r.json()
assert "Renfe" in r.json()["detail"]
Loading