Spaces:
Sleeping
Sleeping
| """El alta de cuentas está cerrada por defecto y sólo la abre una allowlist de emails. | |
| Por qué importa: una cuenta alcanza `/api/interpret`, que gasta cuota de ZeroGPU compartida y, | |
| por la ruta Claude, dinero real. Mientras el alta fue abierta, el techo por usuario | |
| (`limite_interpret_usuario`) protegía una identidad que costaba una petición HTTP acuñar. | |
| """ | |
| from __future__ import annotations | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| from app.config import obtener_config | |
| from app.main import app | |
| ALTA = {"nombre": "Ana", "apellido": "Vet", "password": "clave-segura-1"} | |
| def cliente(): | |
| with TestClient(app) as c: | |
| yield c | |
| def _alta(cliente, email): | |
| return cliente.post("/api/auth/registro", json={**ALTA, "email": email}) | |
| def test_alta_cerrada_por_defecto(cliente): | |
| """Sin tocar nada: el defecto del servicio rechaza el alta.""" | |
| r = _alta(cliente, "desconocida@example.com") | |
| assert r.status_code == 403 | |
| assert "restringida" in r.text | |
| def test_email_en_la_allowlist_puede_darse_de_alta(cliente, monkeypatch): | |
| monkeypatch.setattr(obtener_config(), "registro_allowlist", ["permitida@example.com"]) | |
| r = _alta(cliente, "permitida@example.com") | |
| assert r.status_code == 200, r.text | |
| assert r.json()["ok"] is True | |
| def test_allowlist_ignora_mayusculas_y_espacios(cliente, monkeypatch): | |
| """El email entra por un formulario; comparar crudo dejaría fuera a un aprobado.""" | |
| monkeypatch.setattr(obtener_config(), "registro_allowlist", [" Mixta@Example.COM "]) | |
| assert _alta(cliente, "mixta@example.com").status_code == 200 | |
| def test_fuera_de_la_allowlist_no_revela_si_la_cuenta_existe(cliente, monkeypatch): | |
| """403 tanto si la cuenta existe como si no: el alta no puede ser un oráculo de cuentas. | |
| Si la comprobación de allowlist fuera DESPUÉS de la de existencia, un email no aprobado | |
| distinguiría 409 (existe) de 403 (no existe) y enumeraría la base de usuarios. | |
| """ | |
| monkeypatch.setattr(obtener_config(), "registro_allowlist", ["existente@example.com"]) | |
| assert _alta(cliente, "existente@example.com").status_code == 200 # ya existe a partir de aquí | |
| monkeypatch.setattr(obtener_config(), "registro_allowlist", []) | |
| existente = _alta(cliente, "existente@example.com") | |
| inexistente = _alta(cliente, "jamas-vista@example.com") | |
| assert existente.status_code == 403 | |
| assert inexistente.status_code == 403 | |
| assert existente.text == inexistente.text | |
| def test_registro_abierto_deja_pasar_a_cualquiera(cliente, monkeypatch): | |
| """La vía de escape para desarrollo local sigue funcionando.""" | |
| monkeypatch.setattr(obtener_config(), "registro_abierto", True) | |
| assert _alta(cliente, "cualquiera@example.com").status_code == 200 | |
| def test_allowlist_admite_cadena_separada_por_comas(monkeypatch): | |
| """Forma documentada en .env.example; la de lista JSON ya la cubre el validador compartido.""" | |
| from app.config import Configuracion | |
| cfg = Configuracion(registro_allowlist="uno@example.com, dos@example.com") | |
| assert cfg.emails_registro_permitidos() == {"uno@example.com", "dos@example.com"} | |
| assert cfg.registro_permitido("dos@example.com") | |
| assert not cfg.registro_permitido("tres@example.com") | |