From 309835be8cd219f1541381198ce395c92972e8e6 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Tue, 24 Mar 2026 15:41:47 -0500 Subject: [PATCH] Enhance SQLAlchemy URL validation in Alembic environment - Added a new function to reject documentation placeholder hosts in database URLs, improving error handling for misconfigurations. - Updated the `get_database_url` function to incorporate this new validation, ensuring that users are alerted when using "host" as a placeholder. - Enhanced error messages to provide clearer guidance on valid host configurations. These changes aim to improve the robustness and clarity of database connection handling in the application. --- backend/alembic/env.py | 25 +++++++++++++++++++++++-- backend/tests/README.md | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 6b0d8d8c..07823c7c 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -46,10 +46,27 @@ def _validate_sqlalchemy_url(url: str, env_key: str) -> None: except Exception as e: raise RuntimeError( f"{env_key} no es una URL de SQLAlchemy válida. " - "Ejemplo: postgresql://usuario:clave@host:5432/nombre_bd" + "Ejemplo: postgresql://usuario:clave@127.0.0.1:5432/nombre_bd" ) from e +def _reject_documentation_placeholder_host(url: str, source: str) -> None: + """ + Evita el error críptico de DNS: muchos ejemplos usan @host:5432 como texto literal. + """ + try: + parsed = make_url(url) + except Exception: + return + h = (parsed.host or "").strip().lower() + if h == "host": + raise RuntimeError( + f"{source}: el hostname \"host\" es un placeholder de documentación, no un servidor real. " + "Usa el host alcanzable desde el runner (IP, nombre DNS, servicio en docker-compose, " + "o host.docker.internal si act corre en contenedor y Postgres en tu máquina)." + ) + + def get_database_url(): """Obtiene la URL de la base de datos (PostgreSQL) desde variables de entorno o alembic.ini.""" # CI / pytest: misma URL que los tests (secret TEST_DATABASE_URL) o DATABASE_URL explícita. @@ -60,6 +77,7 @@ def get_database_url(): if raw and raw.strip(): normalized = _normalize_alembic_sqlalchemy_url(raw) _validate_sqlalchemy_url(normalized, env_key) + _reject_documentation_placeholder_host(normalized, env_key) return normalized # Construcción desde settings (CORE_DB_* en .env / entorno) @@ -74,7 +92,9 @@ def get_database_url(): encoded_user = quote_plus(user) encoded_password = quote_plus(password) encoded_db = quote_plus(db) - return f"postgresql+psycopg2://{encoded_user}:{encoded_password}@{host}:{port}/{encoded_db}" + built = f"postgresql+psycopg2://{encoded_user}:{encoded_password}@{host}:{port}/{encoded_db}" + _reject_documentation_placeholder_host(built, "CORE_DB_HOST") + return built except Exception as e: logger.error(f"Error al construir URL: {e}") @@ -87,6 +107,7 @@ def get_database_url(): "sqlalchemy.url en alembic.ini con placeholders ${...} no está soportado." ) + _reject_documentation_placeholder_host(url, "alembic.ini sqlalchemy.url") return url diff --git a/backend/tests/README.md b/backend/tests/README.md index ef790e4b..6e327370 100644 --- a/backend/tests/README.md +++ b/backend/tests/README.md @@ -134,7 +134,7 @@ Si `TEST_DATABASE_URL` apunta a una base con datos previos, los builders reutili - PostgreSQL test database available. - Environment variable: - - `TEST_DATABASE_URL=postgresql://user:pass@host:5432/db_name` + - `TEST_DATABASE_URL=postgresql://user:pass@127.0.0.1:5432/db_name` (sustituye por el host real alcanzable desde el runner; no uses el literal `host` de los ejemplos genéricos) ## Run commands