Initial import of ADIF API reverse-engineering toolkit
This commit is contained in:
158
tests/README.md
Normal file
158
tests/README.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Tests - ADIF API
|
||||
|
||||
Test scripts to validate the functionality of the ADIF API.
|
||||
|
||||
## Active Tests
|
||||
|
||||
### test_endpoints_detailed.py
|
||||
Exhaustive test of all endpoints with complete debug information.
|
||||
|
||||
**Features**:
|
||||
- Shows status codes, headers and JSON response
|
||||
- Tests multiple payload variations
|
||||
- Identifies 400, 401 errors and their causes
|
||||
- Useful for debugging new endpoints
|
||||
|
||||
**Usage**:
|
||||
```bash
|
||||
python3 tests/test_endpoints_detailed.py
|
||||
```
|
||||
|
||||
**Expected output**:
|
||||
- Detailed information for each request
|
||||
- Error analysis with server messages
|
||||
- Differentiation between payload vs permission errors
|
||||
|
||||
---
|
||||
|
||||
### test_onepaths_with_real_trains.py
|
||||
Functional test that gets real trains and tests the `onepaths` endpoint.
|
||||
|
||||
**Features**:
|
||||
- Queries `departures` to get running trains
|
||||
- Extracts `commercialNumber`, `launchingDate`, station codes
|
||||
- Tests `onepaths` with real data
|
||||
- Validates that the endpoint works correctly
|
||||
|
||||
**Usage**:
|
||||
```bash
|
||||
python3 tests/test_onepaths_with_real_trains.py
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Run during the day (when trains are running)
|
||||
- If run at night/early morning may not find trains
|
||||
|
||||
**Expected output**:
|
||||
```
|
||||
======================================================================
|
||||
STEP 1: Getting real trains from Madrid Atocha
|
||||
======================================================================
|
||||
Got 25 trains
|
||||
|
||||
======================================================================
|
||||
STEP 2: Testing onePaths with real trains
|
||||
======================================================================
|
||||
SUCCESS! onePaths works with real data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Archived Tests
|
||||
|
||||
The `archived/` folder contains old tests that were useful during development but are no longer necessary:
|
||||
|
||||
- `test_all_endpoints.py` - Simple version without debug
|
||||
- `test_complete_bodies.py` - Complete payload tests
|
||||
- `test_corrected_api.py` / `test_corrected_api_v2.py` - Previous versions
|
||||
- `test_real_auth.py` - Basic authentication tests
|
||||
- `test_simple.py` - Minimalist test
|
||||
- `test_with_auth_headers.py` - Header validation
|
||||
- `test_without_auth.py` - Test without authentication
|
||||
- `debug_auth.py` - HMAC algorithm debug
|
||||
|
||||
These tests are kept in case they're useful as reference, but the active tests are more complete.
|
||||
|
||||
---
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Basic Template
|
||||
|
||||
```python
|
||||
from adif_auth import AdifAuthenticator
|
||||
import requests
|
||||
import uuid
|
||||
|
||||
ACCESS_KEY = "and20210615"
|
||||
SECRET_KEY = "Jthjtr946RTt"
|
||||
|
||||
def test_endpoint():
|
||||
auth = AdifAuthenticator(access_key=ACCESS_KEY, secret_key=SECRET_KEY)
|
||||
|
||||
url = "https://circulacion.api.adif.es/portroyalmanager/secure/..."
|
||||
payload = {
|
||||
# Your payload here
|
||||
}
|
||||
|
||||
user_id = str(uuid.uuid4())
|
||||
headers = auth.get_auth_headers("POST", url, payload, user_id=user_id)
|
||||
headers["User-key"] = auth.USER_KEY_CIRCULATION
|
||||
|
||||
response = requests.post(url, json=payload, headers=headers, timeout=10)
|
||||
|
||||
assert response.status_code == 200
|
||||
print(f"Test passed: {response.json()}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_endpoint()
|
||||
```
|
||||
|
||||
### Status Code Analysis
|
||||
|
||||
```python
|
||||
if response.status_code == 200:
|
||||
print("SUCCESS - Endpoint functional")
|
||||
data = response.json()
|
||||
|
||||
elif response.status_code == 204:
|
||||
print("NO CONTENT - Correct authentication but no data")
|
||||
|
||||
elif response.status_code == 400:
|
||||
print("BAD REQUEST - Incorrect payload")
|
||||
print(f"Error: {response.json()}")
|
||||
|
||||
elif response.status_code == 401:
|
||||
print("UNAUTHORIZED - No permissions")
|
||||
print(f"Error: {response.json()}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Expected Results
|
||||
|
||||
### Functional Endpoints (200)
|
||||
- `/departures/traffictype/`
|
||||
- `/arrivals/traffictype/`
|
||||
- `/onepaths/` (with real commercialNumber)
|
||||
- `/stationsobservations/`
|
||||
- `/betweenstations/traffictype/`
|
||||
- `/onestation/`
|
||||
- `/allstations/reducedinfo/{token}/`
|
||||
|
||||
### Endpoints returning 204 (Success without data)
|
||||
- `/severalpaths/`
|
||||
- `/compositions/path/`
|
||||
|
||||
---
|
||||
|
||||
## Tips for Creating New Tests
|
||||
|
||||
1. **Use `test_endpoints_detailed.py` as base** - Has good error handling
|
||||
2. **Validate timestamps** - Use milliseconds, not seconds
|
||||
3. **Test with real data** - Like `test_onepaths_with_real_trains.py` does
|
||||
4. **Differentiate errors**:
|
||||
- 400 = Incorrect payload -> Check fields
|
||||
- 401 = No permissions -> Keys don't have access
|
||||
- 204 = No data -> Authentication OK, but empty response
|
||||
|
||||
167
tests/test_api_authenticated.py
Normal file
167
tests/test_api_authenticated.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for ADIF endpoints using HMAC-SHA256 authentication.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
import requests
|
||||
from requests import Response
|
||||
|
||||
# Add project root to sys.path to import adif_auth
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from adif_auth import AdifAuthenticator # noqa: E402
|
||||
|
||||
# Keys extracted with Frida
|
||||
ACCESS_KEY = "and20210615"
|
||||
SECRET_KEY = "Jthjtr946RTt"
|
||||
USER_ID = "0c8c32dce47f8512"
|
||||
|
||||
auth = AdifAuthenticator(ACCESS_KEY, SECRET_KEY)
|
||||
|
||||
|
||||
def build_headers(url: str, payload: Dict[str, Any]) -> Dict[str, str]:
|
||||
"""Create authenticated headers including the correct User-key for the host."""
|
||||
headers = auth.get_auth_headers("POST", url, payload, user_id=USER_ID)
|
||||
headers["User-key"] = auth.get_user_key_for_url(url)
|
||||
return headers
|
||||
|
||||
|
||||
def test_departures() -> None:
|
||||
"""Test departures endpoint."""
|
||||
print("\n" + "=" * 70)
|
||||
print("TEST: Departures from Madrid Atocha (Commuter)")
|
||||
print("=" * 70)
|
||||
|
||||
url = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/departures/traffictype/"
|
||||
|
||||
payload: Dict[str, Any] = {
|
||||
"stationCode": "10200",
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {"pageNumber": 0},
|
||||
"trafficType": "CERCANIAS",
|
||||
}
|
||||
|
||||
headers = build_headers(url, payload)
|
||||
|
||||
print(f"\nURL: {url}")
|
||||
print(f"Payload: {json.dumps(payload)}")
|
||||
print("\nHeaders:")
|
||||
for key, value in headers.items():
|
||||
preview = f"{value[:50]}..." if key == "Authorization" else value
|
||||
print(f" {key}: {preview}")
|
||||
|
||||
try:
|
||||
response: Response = requests.post(
|
||||
url, headers=headers, json=payload, timeout=10
|
||||
)
|
||||
print(f"\nStatus Code: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS!")
|
||||
data = response.json()
|
||||
print("\nResponse (preview):")
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False)[:1000])
|
||||
else:
|
||||
print("❌ ERROR")
|
||||
print(f"Response: {response.text[:500]}")
|
||||
|
||||
except Exception as exc: # pragma: no cover - interactive script
|
||||
print(f"❌ EXCEPTION: {exc}")
|
||||
|
||||
|
||||
def test_station_details() -> None:
|
||||
"""Test station details endpoint."""
|
||||
print("\n" + "=" * 70)
|
||||
print("TEST: Station details Madrid Atocha")
|
||||
print("=" * 70)
|
||||
|
||||
url = "https://estaciones.api.adif.es/portroyalmanager/secure/stations/onestation/"
|
||||
|
||||
payload: Dict[str, Any] = {"stationCode": "10200"}
|
||||
headers = build_headers(url, payload)
|
||||
|
||||
print(f"\nURL: {url}")
|
||||
print(f"Payload: {json.dumps(payload)}")
|
||||
|
||||
try:
|
||||
response: Response = requests.post(
|
||||
url, headers=headers, json=payload, timeout=10
|
||||
)
|
||||
print(f"\nStatus Code: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS!")
|
||||
data = response.json()
|
||||
print("\nResponse (preview):")
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False)[:1000])
|
||||
else:
|
||||
print("❌ ERROR")
|
||||
print(f"Response: {response.text[:500]}")
|
||||
|
||||
except Exception as exc: # pragma: no cover
|
||||
print(f"❌ EXCEPTION: {exc}")
|
||||
|
||||
|
||||
def test_arrivals() -> None:
|
||||
"""Test arrivals endpoint."""
|
||||
print("\n" + "=" * 70)
|
||||
print("TEST: Arrivals to Madrid Atocha (All traffic types)")
|
||||
print("=" * 70)
|
||||
|
||||
url = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/arrivals/traffictype/"
|
||||
|
||||
payload: Dict[str, Any] = {
|
||||
"stationCode": "10200",
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {"pageNumber": 0},
|
||||
"trafficType": "ALL",
|
||||
}
|
||||
|
||||
headers = build_headers(url, payload)
|
||||
|
||||
print(f"\nURL: {url}")
|
||||
|
||||
try:
|
||||
response: Response = requests.post(
|
||||
url, headers=headers, json=payload, timeout=10
|
||||
)
|
||||
print(f"\nStatus Code: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS!")
|
||||
data = response.json()
|
||||
print("\nResponse (preview):")
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False)[:800])
|
||||
else:
|
||||
print("❌ ERROR")
|
||||
print(f"Response: {response.text[:500]}")
|
||||
|
||||
except Exception as exc: # pragma: no cover
|
||||
print(f"❌ EXCEPTION: {exc}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n" + "=" * 70)
|
||||
print("ADIF API TESTS WITH HMAC-SHA256 AUTHENTICATION")
|
||||
print("=" * 70)
|
||||
print("\nUsing keys:")
|
||||
print(f" ACCESS_KEY: {ACCESS_KEY}")
|
||||
print(f" SECRET_KEY: {SECRET_KEY}")
|
||||
print(f" USER_ID: {USER_ID}")
|
||||
|
||||
test_departures()
|
||||
test_station_details()
|
||||
test_arrivals()
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("TESTS COMPLETED")
|
||||
print("=" * 70)
|
||||
185
tests/test_endpoints.py
Normal file
185
tests/test_endpoints.py
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to test different ADIF API endpoints with signed requests.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
import requests
|
||||
from requests import Response
|
||||
|
||||
# Make adif_auth importable when running this script directly
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from adif_auth import AdifAuthenticator # noqa: E402
|
||||
|
||||
ACCESS_KEY: str = "and20210615"
|
||||
SECRET_KEY: str = "Jthjtr946RTt"
|
||||
|
||||
BASE_CIRCULATION = "https://circulacion.api.adif.es"
|
||||
BASE_STATIONS = "https://estaciones.api.adif.es"
|
||||
|
||||
auth = AdifAuthenticator(access_key=ACCESS_KEY, secret_key=SECRET_KEY)
|
||||
|
||||
|
||||
def _headers_for(
|
||||
url: str, payload: Dict[str, Any], use_stations_key: bool = False
|
||||
) -> Dict[str, str]:
|
||||
"""Build authentication headers for a given request body and URL."""
|
||||
headers = auth.get_auth_headers("POST", url, payload)
|
||||
headers["User-key"] = (
|
||||
auth.USER_KEY_STATIONS if use_stations_key else auth.USER_KEY_CIRCULATION
|
||||
)
|
||||
return headers
|
||||
|
||||
|
||||
def test_endpoint(
|
||||
name: str, url: str, payload: Dict[str, Any], use_stations_key: bool = False
|
||||
) -> None:
|
||||
"""Test an endpoint and show a compact preview of the response."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"TEST: {name}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
headers = _headers_for(url, payload, use_stations_key=use_stations_key)
|
||||
|
||||
try:
|
||||
response: Response = requests.post(
|
||||
url, headers=headers, json=payload, timeout=10
|
||||
)
|
||||
print(f"\nStatus: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS")
|
||||
result = response.json()
|
||||
print("\nResponse Preview:")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False)[:1000] + "...")
|
||||
elif response.status_code == 204:
|
||||
print("⚠️ NO CONTENT")
|
||||
else:
|
||||
print("❌ ERROR")
|
||||
print(f"Response: {response.text[:500]}")
|
||||
|
||||
except Exception as exc: # pragma: no cover - simple console diagnostic
|
||||
print(f"❌ EXCEPTION: {exc}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 60)
|
||||
print("ADIF API ENDPOINT TESTS")
|
||||
print("=" * 60)
|
||||
|
||||
test_endpoint(
|
||||
"Departures - Simple Format",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {"pageNumber": 0},
|
||||
"stationCode": "10200",
|
||||
"trafficType": "ALL",
|
||||
},
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Departures - State YES",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
{
|
||||
"commercialService": "YES",
|
||||
"commercialStopType": "YES",
|
||||
"page": {"pageNumber": 0},
|
||||
"stationCode": "10200",
|
||||
"trafficType": "CERCANIAS",
|
||||
},
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Route Details",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/",
|
||||
{
|
||||
"commercialNumber": "03194",
|
||||
"destinationStationCode": "71801",
|
||||
"launchingDate": 1713984000000, # Example timestamp
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Between Stations",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/",
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"destinationStationCode": "71801",
|
||||
"originStationCode": "10200",
|
||||
"page": {"pageNumber": 0},
|
||||
"trafficType": "ALL",
|
||||
},
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Station Details",
|
||||
f"{BASE_STATIONS}/portroyalmanager/secure/stations/onestation/",
|
||||
{"stationCode": "10200"},
|
||||
use_stations_key=True,
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Station Observations",
|
||||
f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/",
|
||||
{"stationCodes": ["10200", "71801"]},
|
||||
use_stations_key=True,
|
||||
)
|
||||
|
||||
test_endpoint(
|
||||
"Train Composition",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/compositions/path/",
|
||||
{
|
||||
"commercialNumber": "03194",
|
||||
"destinationStationCode": "71801",
|
||||
"launchingDate": 1713984000000,
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY")
|
||||
print("=" * 60)
|
||||
print(
|
||||
"""
|
||||
Tests completed. Review results above.
|
||||
|
||||
NOTES:
|
||||
- Some endpoints may require valid codes/numbers
|
||||
- Station codes are numeric (e.g.: 10200 for Madrid Atocha)
|
||||
- Commercial numbers vary by train type
|
||||
- Some data may not be available in real-time
|
||||
|
||||
COMMON STATION CODES:
|
||||
- 10200: Madrid Puerta de Atocha
|
||||
- 10302: Madrid Chamartin
|
||||
- 71801: Barcelona Sants
|
||||
- 50000: Valencia Nord
|
||||
- 11401: Sevilla Santa Justa
|
||||
|
||||
TRAFFIC TYPES:
|
||||
- CERCANIAS: Commuter trains
|
||||
- MEDIA_DISTANCIA: Medium distance
|
||||
- LARGA_DISTANCIA: Long distance
|
||||
- ALL: All types
|
||||
|
||||
STATES:
|
||||
- YES: Only commercial services/stops
|
||||
- NOT: Without commercial services/stops
|
||||
- BOTH: Both types
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
179
tests/test_endpoints_detailed.py
Normal file
179
tests/test_endpoints_detailed.py
Normal file
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Detailed endpoint testing with full error messages and typed helpers.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Sequence
|
||||
|
||||
import requests
|
||||
from requests import Response
|
||||
|
||||
# Add project root to sys.path to import adif_auth
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from adif_auth import AdifAuthenticator # noqa: E402
|
||||
|
||||
ACCESS_KEY = "and20210615"
|
||||
SECRET_KEY = "Jthjtr946RTt"
|
||||
|
||||
|
||||
def test_endpoint_detailed(
|
||||
name: str, url: str, payload: Dict[str, Any], use_stations_key: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Test an endpoint and display detailed information about request and response.
|
||||
|
||||
Returns:
|
||||
True when the endpoint responds with 200, False otherwise.
|
||||
"""
|
||||
auth = AdifAuthenticator(access_key=ACCESS_KEY, secret_key=SECRET_KEY)
|
||||
user_id = str(uuid.uuid4())
|
||||
|
||||
headers = auth.get_auth_headers("POST", url, payload, user_id=user_id)
|
||||
headers["User-key"] = (
|
||||
auth.USER_KEY_STATIONS if use_stations_key else auth.USER_KEY_CIRCULATION
|
||||
)
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f"Testing: {name}")
|
||||
print(f"{'='*70}")
|
||||
print(f"URL: {url}")
|
||||
print(f"Payload: {json.dumps(payload, indent=2)}")
|
||||
|
||||
try:
|
||||
response: Response = requests.post(
|
||||
url, json=payload, headers=headers, timeout=10
|
||||
)
|
||||
print(f"\nStatus Code: {response.status_code}")
|
||||
print(f"Headers: {dict(response.headers)}")
|
||||
|
||||
try:
|
||||
response_json = response.json()
|
||||
print(
|
||||
f"Response Body: {json.dumps(response_json, indent=2, ensure_ascii=False)[:1000]}"
|
||||
)
|
||||
except ValueError:
|
||||
print(f"Response Body (text): {response.text[:500]}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS")
|
||||
return True
|
||||
|
||||
print(f"❌ FAILED - Status {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as exc: # pragma: no cover - console-only diagnostics
|
||||
print(f"❌ ERROR: {exc}")
|
||||
return False
|
||||
|
||||
|
||||
def run_onepaths_variations(today_start: int) -> None:
|
||||
"""Exercise the onepaths endpoint with a variety of payloads."""
|
||||
print("\n\n" + "=" * 70)
|
||||
print("TESTING ONEPATHS WITH DIFFERENT VARIATIONS")
|
||||
print("=" * 70)
|
||||
|
||||
variations: Sequence[Dict[str, Any]] = [
|
||||
{
|
||||
"allControlPoints": True,
|
||||
"commercialNumber": "03194",
|
||||
"destinationStationCode": "71801",
|
||||
"launchingDate": today_start,
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
{
|
||||
"allControlPoints": True,
|
||||
"commercialNumber": None,
|
||||
"destinationStationCode": "71801",
|
||||
"launchingDate": today_start,
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
{
|
||||
"allControlPoints": True,
|
||||
"destinationStationCode": "71801",
|
||||
"launchingDate": today_start,
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
{
|
||||
"allControlPoints": True,
|
||||
"launchingDate": today_start,
|
||||
"originStationCode": "10200",
|
||||
},
|
||||
{"commercialNumber": "03194", "launchingDate": today_start},
|
||||
]
|
||||
|
||||
for index, payload in enumerate(variations, start=1):
|
||||
test_endpoint_detailed(
|
||||
f"OnePaths Variation {index}",
|
||||
"https://circulacion.api.adif.es/portroyalmanager/secure/circulationpathdetails/onepaths/",
|
||||
payload,
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
now = datetime.now()
|
||||
today_start = int(datetime(now.year, now.month, now.day).timestamp() * 1000)
|
||||
tomorrow_start = int(
|
||||
(datetime(now.year, now.month, now.day) + timedelta(days=1)).timestamp() * 1000
|
||||
)
|
||||
|
||||
print("Testing with dates:")
|
||||
print(f"Today (start): {today_start} = {datetime.fromtimestamp(today_start/1000)}")
|
||||
print(
|
||||
f"Tomorrow (start): {tomorrow_start} = {datetime.fromtimestamp(tomorrow_start/1000)}"
|
||||
)
|
||||
|
||||
test_endpoint_detailed(
|
||||
"BetweenStations",
|
||||
"https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/",
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"originStationCode": "10200",
|
||||
"destinationStationCode": "71801",
|
||||
"page": {"pageNumber": 0},
|
||||
"trafficType": "ALL",
|
||||
},
|
||||
)
|
||||
|
||||
run_onepaths_variations(today_start)
|
||||
|
||||
test_endpoint_detailed(
|
||||
"OneStation",
|
||||
"https://estaciones.api.adif.es/portroyalmanager/secure/stations/onestation/",
|
||||
{
|
||||
"stationCode": "10200",
|
||||
"detailedInfo": {
|
||||
"extendedStationInfo": True,
|
||||
"stationActivities": True,
|
||||
"stationBanner": True,
|
||||
"stationCommercialServices": True,
|
||||
"stationInfo": True,
|
||||
"stationServices": True,
|
||||
"stationTransportServices": True,
|
||||
},
|
||||
},
|
||||
use_stations_key=True,
|
||||
)
|
||||
|
||||
test_endpoint_detailed(
|
||||
"OneStation - Simple",
|
||||
"https://estaciones.api.adif.es/portroyalmanager/secure/stations/onestation/",
|
||||
{"stationCode": "10200"},
|
||||
use_stations_key=True,
|
||||
)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("TEST COMPLETED")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
151
tests/test_onepaths_with_real_trains.py
Normal file
151
tests/test_onepaths_with_real_trains.py
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fetch real trains from departures and then test onePaths with those numbers.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Sequence
|
||||
|
||||
import requests
|
||||
from requests import Response
|
||||
|
||||
# Add project root to sys.path to import adif_auth
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from adif_auth import AdifAuthenticator # noqa: E402
|
||||
|
||||
ACCESS_KEY = "and20210615"
|
||||
SECRET_KEY = "Jthjtr946RTt"
|
||||
|
||||
auth = AdifAuthenticator(access_key=ACCESS_KEY, secret_key=SECRET_KEY)
|
||||
|
||||
DEPARTURES_URL = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/departures/traffictype/"
|
||||
ONEPATHS_URL = "https://circulacion.api.adif.es/portroyalmanager/secure/circulationpathdetails/onepaths/"
|
||||
|
||||
|
||||
def fetch_departures(station_code: str, traffic_type: str) -> List[Dict[str, Any]]:
|
||||
"""Fetch current departures from a station."""
|
||||
payload = {
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {"pageNumber": 0},
|
||||
"stationCode": station_code,
|
||||
"trafficType": traffic_type,
|
||||
}
|
||||
|
||||
user_id = str(uuid.uuid4())
|
||||
headers = auth.get_auth_headers("POST", DEPARTURES_URL, payload, user_id=user_id)
|
||||
headers["User-key"] = auth.USER_KEY_CIRCULATION
|
||||
|
||||
response: Response = requests.post(
|
||||
DEPARTURES_URL, json=payload, headers=headers, timeout=10
|
||||
)
|
||||
if response.status_code != 200:
|
||||
raise RuntimeError(
|
||||
f"Error getting departures: {response.status_code} - {response.text}"
|
||||
)
|
||||
|
||||
data = response.json()
|
||||
return data.get("circulations", [])
|
||||
|
||||
|
||||
def test_onepaths_with_trains(
|
||||
trains: Sequence[Dict[str, Any]], launching_date: int
|
||||
) -> None:
|
||||
"""Run onepaths queries against a subset of the provided trains."""
|
||||
for index, train in enumerate(trains[:3], start=1):
|
||||
commercial_number = train.get("commercialNumber")
|
||||
destination = train.get("destination", {})
|
||||
dest_code = destination.get("stationCode")
|
||||
origin = train.get("origin", {})
|
||||
origin_code = origin.get("stationCode")
|
||||
|
||||
payload_onepaths: Dict[str, Any] = {
|
||||
"allControlPoints": True,
|
||||
"commercialNumber": commercial_number,
|
||||
"destinationStationCode": dest_code,
|
||||
"launchingDate": launching_date,
|
||||
"originStationCode": origin_code,
|
||||
}
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f"Test {index}: Train {commercial_number}")
|
||||
print(f"{'='*70}")
|
||||
print(f"Payload: {json.dumps(payload_onepaths, indent=2)}")
|
||||
|
||||
user_id = str(uuid.uuid4())
|
||||
headers = auth.get_auth_headers(
|
||||
"POST", ONEPATHS_URL, payload_onepaths, user_id=user_id
|
||||
)
|
||||
headers["User-key"] = auth.USER_KEY_CIRCULATION
|
||||
|
||||
response: Response = requests.post(
|
||||
ONEPATHS_URL, json=payload_onepaths, headers=headers, timeout=10
|
||||
)
|
||||
print(f"\nStatus: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS!")
|
||||
try:
|
||||
data = response.json()
|
||||
print(
|
||||
f"Response: {json.dumps(data, indent=2, ensure_ascii=False)[:2000]}"
|
||||
)
|
||||
except ValueError:
|
||||
print(f"Response text: {response.text[:500]}")
|
||||
elif response.status_code == 204:
|
||||
print("⚠️ 204 No Content - Correct authentication but no data")
|
||||
else:
|
||||
print(f"❌ FAILED - Status {response.status_code}")
|
||||
try:
|
||||
print(f"Error: {response.json()}")
|
||||
except ValueError:
|
||||
print(f"Response text: {response.text}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 70)
|
||||
print("STEP 1: Getting real trains from Madrid Atocha")
|
||||
print("=" * 70)
|
||||
|
||||
trains = fetch_departures(station_code="10200", traffic_type="AVLDMD")
|
||||
print(f"✅ Retrieved {len(trains)} trains\n")
|
||||
|
||||
print("First 5 trains:")
|
||||
for i, train in enumerate(trains[:5], start=1):
|
||||
commercial_number = train.get("commercialNumber")
|
||||
destination = train.get("destination", {})
|
||||
dest_name = destination.get("longName", "Unknown")
|
||||
origin = train.get("origin", {})
|
||||
origin_name = origin.get("longName", "Unknown")
|
||||
planned_time = train.get("plannedTime", "Unknown")
|
||||
|
||||
print(f"\n{i}. Train {commercial_number}")
|
||||
print(f" Origin: {origin_name}")
|
||||
print(f" Destination: {dest_name}")
|
||||
print(f" Departure time: {planned_time}")
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("STEP 2: Testing onePaths with real trains")
|
||||
print("=" * 70)
|
||||
|
||||
now = datetime.now()
|
||||
launching_date = int(datetime(now.year, now.month, now.day).timestamp() * 1000)
|
||||
test_onepaths_with_trains(trains, launching_date)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("TEST COMPLETE")
|
||||
print("=" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as exc: # pragma: no cover - console script
|
||||
print(f"❌ Error running test: {exc}")
|
||||
Reference in New Issue
Block a user