Ошибка валидации: 422 без эха тела запроса вместо 500
Если JSON-эндпойнт получал тело с Content-Type text/plain (так шлёт HTML-форма), сервер отвечал 500. _validation_handler клал exc.errors() в details как есть, а в input там лежало сырое тело типа bytes, которое JSONResponse не сериализует. Затронуты все эндпойнты с телом, в том числе /api/auth/telegram и /api/admin/auth/login. Запрос и раньше отклонялся, сессия не создавалась, но вместо понятной 422 в логе появлялся трейсбек. Тот же input возвращал клиенту тело запроса целиком. Неполная форма входа отправляла пароль обратно в ответе 422, откуда он мог осесть в логах прокси и инструментах отладки. Теперь в details идут только type, loc и msg. input и ctx (там бывают объекты исключений — та же проблема сериализации) в ответ не попадают. Конверт и код VALIDATION_ERROR прежние, фронт details валидации не читает. Тесты: text/plain на двух эндпойнтах даёт 422, пароль не возвращается, loc по-прежнему указывает на поле. На старом коде первые два падают. #52 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
"""Ошибки валидации запроса: всегда 422 в едином конверте и без эха тела запроса."""
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_non_json_body_is_422_not_500(client: TestClient):
|
||||
"""HTML-форма шлёт text/plain: тело приходит сырыми bytes, ответ раньше падал в 500."""
|
||||
for path, raw in [
|
||||
("/api/auth/telegram", '{"id": 1}'),
|
||||
("/api/admin/auth/login", '{"username": "a", "password": "b"}'),
|
||||
]:
|
||||
r = client.post(path, content=raw, headers={"Content-Type": "text/plain"})
|
||||
assert r.status_code == 422, (path, r.text)
|
||||
assert r.json()["error"]["code"] == "VALIDATION_ERROR"
|
||||
|
||||
|
||||
def test_validation_error_does_not_echo_body(client: TestClient):
|
||||
secret = "very-secret-password"
|
||||
r = client.post("/api/admin/auth/login", json={"password": secret})
|
||||
assert r.status_code == 422, r.text
|
||||
assert secret not in r.text
|
||||
|
||||
details = r.json()["error"]["details"]
|
||||
assert details and all(set(d) == {"type", "loc", "msg"} for d in details)
|
||||
|
||||
|
||||
def test_validation_error_points_to_field(client: TestClient):
|
||||
r = client.post("/api/admin/auth/login", json={"password": "x"})
|
||||
locs = [d["loc"] for d in r.json()["error"]["details"]]
|
||||
assert ["body", "username"] in locs
|
||||
Reference in New Issue
Block a user