Primer paso de la investigacion. Se aportan el .apk, las carpetas con el apk extraido y el apk descompilado. El archivo API_DOCUMENTATION.md es un archivo donde se anotaran los descubrimientos del funcionamiento de la API, y los .py son scripts para probar la funcionalidad de la API con los métodos que vayamos encontrando. Finalmente, los archivos .js son scripts de Frida para extraer informacion de la APP durante la ejecucion.

This commit is contained in:
2025-12-04 13:59:54 +01:00
parent f2fd1c3bf5
commit e0133d2ca2
10432 changed files with 1019085 additions and 1 deletions

View File

@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""
Sistema de autenticación HMAC-SHA256 para API de Adif
Basado en ElcanoAuth.java
"""
import hmac
import hashlib
import json
from datetime import datetime
from typing import Dict
class AdifAuthenticator:
"""Autenticador para la API de Adif usando HMAC-SHA256"""
def __init__(self, access_key: str, secret_key: str, user_id: str):
self.access_key = access_key
self.secret_key = secret_key
self.user_id = user_id
self.client = "AndroidElcanoApp"
def _format_payload(self, payload: str) -> str:
"""Formatear payload (eliminar espacios, saltos de línea)"""
return payload.replace(" ", "").replace("\n", "").replace("\r", "")
def _to_hex(self, data: str) -> str:
"""Calcular SHA256 hash en hexadecimal"""
return hashlib.sha256(data.encode('utf-8')).hexdigest()
def _hmac_sha256(self, key: bytes, message: str) -> bytes:
"""Calcular HMAC-SHA256"""
return hmac.new(key, message.encode('utf-8'), hashlib.sha256).digest()
def _get_signature_key(self, date_simple: str) -> bytes:
"""Derivar clave de firma"""
# kDate = HMAC-SHA256(secret_key, date)
k_date = self._hmac_sha256(self.secret_key.encode('utf-8'), date_simple)
# kClient = HMAC-SHA256(kDate, client)
k_client = self._hmac_sha256(k_date, self.client)
# kSigning = HMAC-SHA256(kClient, "elcano_request")
k_signing = self._hmac_sha256(k_client, "elcano_request")
return k_signing
def _prepare_canonical_request(self, method: str, path: str, params: str,
host: str, date: str, payload: str) -> tuple:
"""Preparar canonical request"""
# Headers canónicos (deben estar en orden)
canonical_headers = (
f"content-type:application/json;charset=utf-8\n"
f"x-elcano-client:{self.client}\n"
f"x-elcano-date:{date}\n"
f"x-elcano-host:{host}\n"
f"x-elcano-userid:{self.user_id}\n"
)
signed_headers = "content-type;x-elcano-client;x-elcano-date;x-elcano-host;x-elcano-userid"
# Formatear payload
formatted_payload = self._format_payload(payload)
payload_hash = self._to_hex(formatted_payload)
# Canonical request
canonical_request = (
f"{method}\n"
f"{path}\n"
f"{params}\n"
f"{canonical_headers}"
f"{signed_headers}\n"
f"{payload_hash}"
)
return canonical_request, signed_headers
def _prepare_string_to_sign(self, canonical_request: str, date: str, date_simple: str) -> str:
"""Preparar string to sign"""
canonical_hash = self._to_hex(canonical_request)
string_to_sign = (
f"HMAC-SHA256\n"
f"{date}\n"
f"{date_simple}/{self.client}/{self.user_id}/elcano_request\n"
f"{canonical_hash}"
)
return string_to_sign
def _calculate_signature(self, string_to_sign: str, date_simple: str) -> str:
"""Calcular firma"""
signing_key = self._get_signature_key(date_simple)
signature = self._hmac_sha256(signing_key, string_to_sign)
return signature.hex()
def sign_request(self, method: str, host: str, path: str,
params: str = "", payload: str = "") -> Dict[str, str]:
"""
Firmar una petición HTTP
Args:
method: Método HTTP (GET, POST, etc.)
host: Host (ej: circulacion.api.adif.es)
path: Path de la petición
params: Query parameters (vacío si no hay)
payload: Body JSON (vacío para GET)
Returns:
Dict con todos los headers necesarios
"""
# Timestamps
now = datetime.utcnow()
date = now.strftime("%Y%m%dT%H%M%SZ")
date_simple = now.strftime("%Y%m%d")
# Canonical request
canonical_request, signed_headers = self._prepare_canonical_request(
method, path, params, host, date, payload
)
# String to sign
string_to_sign = self._prepare_string_to_sign(canonical_request, date, date_simple)
# Signature
signature = self._calculate_signature(string_to_sign, date_simple)
# Authorization header
authorization = (
f"HMAC-SHA256 "
f"Credential={self.access_key}/{date_simple}/{self.client}/{self.user_id}/elcano_request,"
f"SignedHeaders={signed_headers},"
f"Signature={signature}"
)
return {
"X-Elcano-Host": host,
"Content-type": "application/json;charset=utf-8",
"X-Elcano-Client": self.client,
"X-Elcano-Date": date,
"X-Elcano-UserId": self.user_id,
"Authorization": authorization
}
if __name__ == "__main__":
# Test con las claves extraídas
auth = AdifAuthenticator(
access_key="and20210615",
secret_key="Jthjtr946RTt",
user_id="0c8c32dce47f8512"
)
# Ejemplo de firma
payload = json.dumps({
"stationCode": "10200",
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"trafficType": "CERCANIAS"
})
headers = auth.sign_request(
method="POST",
host="circulacion.api.adif.es",
path="/portroyalmanager/secure/circulationpaths/departures/traffictype/",
payload=payload
)
print("Headers generados:")
for key, value in headers.items():
print(f"{key}: {value}")

View File

@@ -0,0 +1,431 @@
#!/usr/bin/env python3
"""
Cliente Python para la API de Adif (Elcano)
Obtenido mediante ingeniería reversa de la aplicación móvil
"""
import requests
import json
from typing import Optional, Dict, Any, List
from datetime import datetime
from enum import Enum
class TrafficType(Enum):
"""Tipos de tráfico ferroviario"""
CERCANIAS = "CERCANIAS"
MEDIA_DISTANCIA = "MEDIA_DISTANCIA"
LARGA_DISTANCIA = "LARGA_DISTANCIA"
ALL = "ALL"
class State(Enum):
"""Estados para filtros"""
YES = "YES"
NO = "NO"
ALL = "ALL"
class AdifClient:
"""Cliente para interactuar con la API de Adif"""
# URLs base
BASE_URL_STATIONS = "https://estaciones.api.adif.es"
BASE_URL_CIRCULATION = "https://circulacion.api.adif.es"
BASE_URL_ELCANOWEB = "https://elcanoweb.adif.es/api"
BASE_URL_AVISA = "https://avisa.adif.es"
# User keys
USER_KEY_CIRCULATIONS = "f4ce9fbfa9d721e39b8984805901b5df"
USER_KEY_STATIONS = "0d021447a2fd2ac64553674d5a0c1a6f"
# Tokens
REGISTRATION_TOKEN = "b9034774-c6e4-4663-a1a8-74bf7102651b"
AVISA_BASIC_TOKEN = "YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg=="
SUBSCRIPTIONS_BASIC_TOKEN = "ZGVpbW9zOmRlaW1vc3R0"
def __init__(self, debug: bool = False):
"""
Inicializar el cliente
Args:
debug: Si True, imprime información de depuración
"""
self.debug = debug
self.session = requests.Session()
def _get_headers_stations(self) -> Dict[str, str]:
"""Headers para endpoints de estaciones"""
return {
"Content-Type": "application/json;charset=utf-8",
"User-key": self.USER_KEY_STATIONS
}
def _get_headers_circulations(self) -> Dict[str, str]:
"""Headers para endpoints de circulaciones"""
return {
"Content-Type": "application/json;charset=utf-8",
"User-key": self.USER_KEY_CIRCULATIONS
}
def _get_headers_avisa(self) -> Dict[str, str]:
"""Headers para endpoints de Avisa"""
return {
"Content-Type": "application/json;charset=utf-8",
"Authorization": f"Basic {self.AVISA_BASIC_TOKEN}"
}
def _log(self, message: str):
"""Log de depuración"""
if self.debug:
print(f"[DEBUG] {message}")
def _request(self, method: str, url: str, headers: Dict[str, str],
data: Optional[Dict] = None, params: Optional[Dict] = None) -> Optional[Dict]:
"""
Realizar petición HTTP
Args:
method: Método HTTP (GET, POST, etc.)
url: URL completa
headers: Headers HTTP
data: Body JSON (opcional)
params: Query parameters (opcional)
Returns:
Respuesta JSON o None si hay error
"""
try:
self._log(f"{method} {url}")
if data:
self._log(f"Body: {json.dumps(data, indent=2)}")
response = self.session.request(
method=method,
url=url,
headers=headers,
json=data,
params=params,
timeout=30
)
self._log(f"Status: {response.status_code}")
if response.status_code == 200:
return response.json()
else:
self._log(f"Error: {response.text}")
return {
"error": True,
"status_code": response.status_code,
"message": response.text
}
except Exception as e:
self._log(f"Exception: {str(e)}")
return {"error": True, "message": str(e)}
# ==================== ESTACIONES ====================
def get_all_stations(self) -> Optional[Dict]:
"""
Obtener todas las estaciones
Returns:
Listado de estaciones
"""
url = f"{self.BASE_URL_STATIONS}/portroyalmanager/secure/stations/allstations/reducedinfo/{self.REGISTRATION_TOKEN}/"
return self._request("GET", url, self._get_headers_stations())
def get_station_details(self, station_code: str) -> Optional[Dict]:
"""
Obtener detalles de una estación
Args:
station_code: Código de la estación
Returns:
Detalles de la estación
"""
url = f"{self.BASE_URL_STATIONS}/portroyalmanager/secure/stations/onestation/"
data = {"stationCode": station_code}
return self._request("POST", url, self._get_headers_stations(), data=data)
def get_station_observations(self, station_code: str) -> Optional[Dict]:
"""
Obtener observaciones de una estación
Args:
station_code: Código de la estación
Returns:
Observaciones de la estación
"""
url = f"{self.BASE_URL_STATIONS}/portroyalmanager/secure/stationsobservations/"
data = {"stationCode": station_code}
return self._request("POST", url, self._get_headers_stations(), data=data)
# ==================== CIRCULACIONES ====================
def get_departures(self,
station_code: str,
traffic_type: TrafficType = TrafficType.ALL,
commercial_service: State = State.ALL,
commercial_stop_type: State = State.ALL,
page: int = 0,
size: int = 20,
origin_station: Optional[str] = None,
destination_station: Optional[str] = None) -> Optional[Dict]:
"""
Obtener salidas desde una estación
Args:
station_code: Código de la estación
traffic_type: Tipo de tráfico (CERCANIAS, MEDIA_DISTANCIA, etc.)
commercial_service: Filtro de servicio comercial
commercial_stop_type: Filtro de tipo de parada comercial
page: Número de página
size: Tamaño de página
origin_station: Estación origen (opcional)
destination_station: Estación destino (opcional)
Returns:
Salidas de trenes
"""
url = f"{self.BASE_URL_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/"
data = {
"commercialService": commercial_service.value,
"commercialStopType": commercial_stop_type.value,
"stationCode": station_code,
"page": {
"page": page,
"size": size
},
"trafficType": traffic_type.value
}
if origin_station:
data["originStationCode"] = origin_station
if destination_station:
data["destinationStationCode"] = destination_station
return self._request("POST", url, self._get_headers_circulations(), data=data)
def get_arrivals(self,
station_code: str,
traffic_type: TrafficType = TrafficType.ALL,
commercial_service: State = State.ALL,
commercial_stop_type: State = State.ALL,
page: int = 0,
size: int = 20,
origin_station: Optional[str] = None,
destination_station: Optional[str] = None) -> Optional[Dict]:
"""
Obtener llegadas a una estación
Args:
station_code: Código de la estación
traffic_type: Tipo de tráfico
commercial_service: Filtro de servicio comercial
commercial_stop_type: Filtro de tipo de parada comercial
page: Número de página
size: Tamaño de página
origin_station: Estación origen (opcional)
destination_station: Estación destino (opcional)
Returns:
Llegadas de trenes
"""
url = f"{self.BASE_URL_CIRCULATION}/portroyalmanager/secure/circulationpaths/arrivals/traffictype/"
data = {
"commercialService": commercial_service.value,
"commercialStopType": commercial_stop_type.value,
"stationCode": station_code,
"page": {
"page": page,
"size": size
},
"trafficType": traffic_type.value
}
if origin_station:
data["originStationCode"] = origin_station
if destination_station:
data["destinationStationCode"] = destination_station
return self._request("POST", url, self._get_headers_circulations(), data=data)
def get_between_stations(self,
origin_station: str,
destination_station: str,
traffic_type: TrafficType = TrafficType.ALL,
commercial_service: State = State.ALL,
commercial_stop_type: State = State.ALL,
page: int = 0,
size: int = 20) -> Optional[Dict]:
"""
Obtener trenes entre dos estaciones
Args:
origin_station: Estación origen
destination_station: Estación destino
traffic_type: Tipo de tráfico
commercial_service: Filtro de servicio comercial
commercial_stop_type: Filtro de tipo de parada comercial
page: Número de página
size: Tamaño de página
Returns:
Trenes entre estaciones
"""
url = f"{self.BASE_URL_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/"
data = {
"commercialService": commercial_service.value,
"commercialStopType": commercial_stop_type.value,
"originStationCode": origin_station,
"destinationStationCode": destination_station,
"page": {
"page": page,
"size": size
},
"trafficType": traffic_type.value
}
return self._request("POST", url, self._get_headers_circulations(), data=data)
def get_path_details(self,
commercial_number: Optional[str] = None,
origin_station: Optional[str] = None,
destination_station: Optional[str] = None,
launching_date: Optional[int] = None,
all_control_points: bool = False) -> Optional[Dict]:
"""
Obtener detalles de una ruta/tren específico
Args:
commercial_number: Número comercial del tren
origin_station: Estación origen
destination_station: Estación destino
launching_date: Fecha de salida (timestamp en milisegundos)
all_control_points: Si mostrar todos los puntos de control
Returns:
Detalles de la ruta
"""
url = f"{self.BASE_URL_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/"
data = {
"allControlPoints": all_control_points
}
if commercial_number:
data["commercialNumber"] = commercial_number
if origin_station:
data["originStationCode"] = origin_station
if destination_station:
data["destinationStationCode"] = destination_station
if launching_date:
data["launchingDate"] = launching_date
return self._request("POST", url, self._get_headers_circulations(), data=data)
def get_composition(self,
commercial_number: Optional[str] = None,
origin_station: Optional[str] = None,
destination_station: Optional[str] = None,
launching_date: Optional[int] = None) -> Optional[Dict]:
"""
Obtener composición de un tren (vagones, etc.)
Args:
commercial_number: Número comercial del tren
origin_station: Estación origen
destination_station: Estación destino
launching_date: Fecha de salida (timestamp en milisegundos)
Returns:
Composición del tren
"""
url = f"{self.BASE_URL_CIRCULATION}/portroyalmanager/secure/circulationpaths/compositions/path/"
data = {}
if commercial_number:
data["commercialNumber"] = commercial_number
if origin_station:
data["originStationCode"] = origin_station
if destination_station:
data["destinationStationCode"] = destination_station
if launching_date:
data["launchingDate"] = launching_date
return self._request("POST", url, self._get_headers_circulations(), data=data)
# ==================== AVISA ====================
def avisa_get_stations(self) -> Optional[Dict]:
"""
Obtener estaciones de Avisa
Returns:
Estaciones de Avisa
"""
url = f"{self.BASE_URL_AVISA}/avisa-ws/api/v1/station"
return self._request("GET", url, self._get_headers_avisa())
def avisa_get_categories(self) -> Optional[Dict]:
"""
Obtener categorías de estaciones
Returns:
Categorías
"""
url = f"{self.BASE_URL_AVISA}/avisa-ws/api/v1/category"
return self._request("GET", url, self._get_headers_avisa())
def avisa_get_incidences(self) -> Optional[Dict]:
"""
Obtener incidencias
Returns:
Lista de incidencias
"""
url = f"{self.BASE_URL_AVISA}/avisa-ws/api/v1/incidence"
return self._request("GET", url, self._get_headers_avisa())
def main():
"""Ejemplo de uso"""
print("=== Cliente Adif API ===\n")
# Crear cliente con modo debug
client = AdifClient(debug=True)
# Ejemplo: Obtener todas las estaciones
print("\n1. Intentando obtener todas las estaciones...")
stations = client.get_all_stations()
if stations and not stations.get("error"):
print(f"✓ Encontradas {len(stations.get('stations', []))} estaciones")
else:
print(f"✗ Error: {stations}")
# Ejemplo: Obtener salidas de Madrid Atocha (código: 10200)
print("\n2. Intentando obtener salidas de Madrid Atocha...")
departures = client.get_departures(
station_code="10200",
traffic_type=TrafficType.CERCANIAS,
size=5
)
if departures and not departures.get("error"):
print(f"✓ Obtenidas salidas")
print(json.dumps(departures, indent=2, ensure_ascii=False)[:500] + "...")
else:
print(f"✗ Error: {departures}")
# Ejemplo: Obtener estaciones de Avisa
print("\n3. Intentando obtener estaciones de Avisa...")
avisa_stations = client.avisa_get_stations()
if avisa_stations and not avisa_stations.get("error"):
print(f"✓ Obtenidas estaciones de Avisa")
else:
print(f"✗ Error: {avisa_stations}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""
Test de endpoints de Adif con autenticación HMAC-SHA256
"""
import requests
import json
from adif_auth import AdifAuthenticator
# Claves extraídas con Frida
ACCESS_KEY = "and20210615"
SECRET_KEY = "Jthjtr946RTt"
USER_ID = "0c8c32dce47f8512"
# Crear autenticador
auth = AdifAuthenticator(ACCESS_KEY, SECRET_KEY, USER_ID)
def test_departures():
"""Probar endpoint de salidas"""
print("\n" + "="*70)
print("TEST: Salidas de Madrid Atocha (Cercanías)")
print("="*70)
host = "circulacion.api.adif.es"
path = "/portroyalmanager/secure/circulationpaths/departures/traffictype/"
url = f"https://{host}{path}"
payload = {
"stationCode": "10200",
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"trafficType": "CERCANIAS"
}
payload_str = json.dumps(payload)
# Firmar petición
headers = auth.sign_request(
method="POST",
host=host,
path=path,
payload=payload_str
)
print(f"\nURL: {url}")
print(f"Payload: {payload_str}")
print(f"\nHeaders:")
for k, v in headers.items():
if k == "Authorization":
print(f" {k}: {v[:50]}...")
else:
print(f" {k}: {v}")
# Hacer petición
try:
response = requests.post(url, headers=headers, data=payload_str, timeout=10)
print(f"\nStatus Code: {response.status_code}")
if response.status_code == 200:
print("✅ SUCCESS!")
data = response.json()
print(f"\nRespuesta (preview):")
print(json.dumps(data, indent=2, ensure_ascii=False)[:1000])
else:
print(f"❌ ERROR")
print(f"Response: {response.text[:500]}")
except Exception as e:
print(f"❌ EXCEPTION: {e}")
def test_station_details():
"""Probar endpoint de detalles de estación"""
print("\n" + "="*70)
print("TEST: Detalles de estación Madrid Atocha")
print("="*70)
host = "estaciones.api.adif.es"
path = "/portroyalmanager/secure/stations/onestation/"
url = f"https://{host}{path}"
payload = {"stationCode": "10200"}
payload_str = json.dumps(payload)
headers = auth.sign_request(
method="POST",
host=host,
path=path,
payload=payload_str
)
print(f"\nURL: {url}")
print(f"Payload: {payload_str}")
try:
response = requests.post(url, headers=headers, data=payload_str, timeout=10)
print(f"\nStatus Code: {response.status_code}")
if response.status_code == 200:
print("✅ SUCCESS!")
data = response.json()
print(f"\nRespuesta (preview):")
print(json.dumps(data, indent=2, ensure_ascii=False)[:1000])
else:
print(f"❌ ERROR")
print(f"Response: {response.text[:500]}")
except Exception as e:
print(f"❌ EXCEPTION: {e}")
def test_arrivals():
"""Probar endpoint de llegadas"""
print("\n" + "="*70)
print("TEST: Llegadas a Madrid Atocha (Todos los tipos)")
print("="*70)
host = "circulacion.api.adif.es"
path = "/portroyalmanager/secure/circulationpaths/arrivals/traffictype/"
url = f"https://{host}{path}"
payload = {
"stationCode": "10200",
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"trafficType": "ALL"
}
payload_str = json.dumps(payload)
headers = auth.sign_request(
method="POST",
host=host,
path=path,
payload=payload_str
)
print(f"\nURL: {url}")
try:
response = requests.post(url, headers=headers, data=payload_str, timeout=10)
print(f"\nStatus Code: {response.status_code}")
if response.status_code == 200:
print("✅ SUCCESS!")
data = response.json()
print(f"\nRespuesta (preview):")
print(json.dumps(data, indent=2, ensure_ascii=False)[:800])
else:
print(f"❌ ERROR")
print(f"Response: {response.text[:500]}")
except Exception as e:
print(f"❌ EXCEPTION: {e}")
if __name__ == "__main__":
print("\n" + "="*70)
print("PRUEBAS DE API ADIF CON AUTENTICACIÓN HMAC-SHA256")
print("="*70)
print(f"\nUsando claves:")
print(f" ACCESS_KEY: {ACCESS_KEY}")
print(f" SECRET_KEY: {SECRET_KEY}")
print(f" USER_ID: {USER_ID}")
# Ejecutar tests
test_departures()
test_station_details()
test_arrivals()
print("\n" + "="*70)
print("TESTS COMPLETADOS")
print("="*70)

View File

@@ -0,0 +1,199 @@
#!/usr/bin/env python3
"""
Script para probar diferentes endpoints de la API de Adif
"""
import requests
import json
from datetime import datetime
# Headers descubiertos
HEADERS_CIRCULATION = {
"Content-Type": "application/json;charset=utf-8",
"User-key": "f4ce9fbfa9d721e39b8984805901b5df"
}
HEADERS_STATIONS = {
"Content-Type": "application/json;charset=utf-8",
"User-key": "0d021447a2fd2ac64553674d5a0c1a6f"
}
HEADERS_AVISA = {
"Content-Type": "application/json;charset=utf-8",
"Authorization": "Basic YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg=="
}
# URLs base
BASE_CIRCULATION = "https://circulacion.api.adif.es"
BASE_STATIONS = "https://estaciones.api.adif.es"
BASE_AVISA = "https://avisa.adif.es"
def test_endpoint(name, method, url, headers, data=None):
"""Probar un endpoint y mostrar resultado"""
print(f"\n{'='*60}")
print(f"TEST: {name}")
print(f"{'='*60}")
print(f"Method: {method}")
print(f"URL: {url}")
print(f"Headers: {json.dumps(headers, indent=2)}")
if data:
print(f"Body: {json.dumps(data, indent=2)}")
try:
if method == "GET":
response = requests.get(url, headers=headers, timeout=10)
elif method == "POST":
response = requests.post(url, headers=headers, json=data, timeout=10)
else:
print(f"❌ Método {method} no soportado")
return
print(f"\nStatus: {response.status_code}")
if response.status_code == 200:
print("✅ SUCCESS")
result = response.json()
print(f"\nResponse Preview:")
print(json.dumps(result, indent=2, ensure_ascii=False)[:1000] + "...")
else:
print(f"❌ ERROR")
print(f"Response: {response.text[:500]}")
except Exception as e:
print(f"❌ EXCEPTION: {str(e)}")
def main():
print("=" * 60)
print("PRUEBAS DE ENDPOINTS DE ADIF API")
print("=" * 60)
# Test 1: Obtener salidas con diferentes formatos de body
test_endpoint(
"Salidas - Formato Simple",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
HEADERS_CIRCULATION,
{
"stationCode": "10200",
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"trafficType": "CERCANIAS"
}
)
# Test 2: Probar con State YES
test_endpoint(
"Salidas - State YES",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
HEADERS_CIRCULATION,
{
"stationCode": "10200",
"commercialService": "YES",
"commercialStopType": "YES",
"page": {"pageNumber": 0},
"trafficType": "ALL"
}
)
# Test 3: Detalles de una ruta específica
test_endpoint(
"Detalles de Ruta",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/",
HEADERS_CIRCULATION,
{
"commercialNumber": "C1",
"allControlPoints": False
}
)
# Test 4: Entre estaciones
test_endpoint(
"Entre Estaciones",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/",
HEADERS_CIRCULATION,
{
"originStationCode": "10200",
"destinationStationCode": "10302",
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"trafficType": "ALL"
}
)
# Test 5: Detalles de estación
test_endpoint(
"Detalles de Estación",
"POST",
f"{BASE_STATIONS}/portroyalmanager/secure/stations/onestation/",
HEADERS_STATIONS,
{
"stationCode": "10200"
}
)
# Test 6: Observaciones de estación
test_endpoint(
"Observaciones de Estación",
"POST",
f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/",
HEADERS_STATIONS,
{
"stationCode": "10200"
}
)
# Test 7: Composición de tren
test_endpoint(
"Composición de Tren",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/compositions/path/",
HEADERS_CIRCULATION,
{
"commercialNumber": "AVE 1001",
"originStationCode": "10200",
"destinationStationCode": "71801"
}
)
print("\n" + "="*60)
print("RESUMEN")
print("="*60)
print("""
Las pruebas han finalizado. Revisa los resultados arriba.
NOTAS:
- Algunos endpoints pueden requerir códigos/números válidos
- Los códigos de estación son numéricos (ej: 10200 para Madrid Atocha)
- Los números comerciales varían según el tipo de tren
- Algunos datos pueden no estar disponibles en tiempo real
CÓDIGOS DE ESTACIÓN COMUNES:
- 10200: Madrid Puerta de Atocha
- 10302: Madrid Chamartín
- 71801: Barcelona Sants
- 50000: Valencia Nord
- 11401: Sevilla Santa Justa
TIPOS DE TRÁFICO:
- CERCANIAS: Trenes de cercanías
- MEDIA_DISTANCIA: Media distancia
- LARGA_DISTANCIA: Larga distancia
- ALL: Todos los tipos
ESTADOS:
- YES: Solo servicios/paradas comerciales
- NOT: Sin servicios/paradas comerciales
- BOTH: Ambos tipos
""")
if __name__ == "__main__":
main()