gendesign/backend/tests/test_health.py
bot-backend 3f5f099392
Some checks failed
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Failing after 2m34s
CI Trade-In / backend-tests (pull_request) Successful in 5m4s
CI / backend-tests (pull_request) Successful in 16m49s
fix(health): HEAD /health на верном бэкенде (Site Finder) + честные заголовки
Review-разбор ветки fix/tradein-uptime-honest-green:

1. [HIGH] Прод-симптом `HEAD gendsgn.ru/health -> 405` обслуживает Site
   Finder (Caddyfile:60 `handle /health { reverse_proxy backend:8000 }`),
   а предыдущий коммит правил только tradein-mvp/backend, чей /health наружу
   не проксируется вообще. Добавлен @app.head("/health") в backend/app/main.py
   рядом с существующим @app.get — эмпирически подтверждено (uv run pytest):
   HEAD было 405, стало 200. tradein-mvp фикс не откачен (безвреден, годится
   для будущего internal-caller), но обвязан комментарием, что реальный
   прод-путь чинится не там.

2. [LOW] Response(status_code=200) без media_type отдавал HEAD без
   Content-Type, тогда как GET отдаёт application/json — расходится с
   заявленным в комментарии RFC 9110 §9.3.2. Добавлен media_type в обоих
   бэкендах; Content-Length сознательно не подгоняем под байты GET-ответа
   (payload header field, RFC разрешает опускать для HEAD) — не дублируем
   сборку payload ради байт-в-байт соответствия.

Тесты: test_health_head_ok_no_body добавлен в backend/tests/test_health.py
(Site Finder) — RED-check (git stash app/main.py) воспроизводит прод-баг
1:1: assert 405 == 200. tradein-mvp/backend/tests/test_health_endpoint.py
дополнен проверкой Content-Type. uv run pytest — все зелёные.
2026-08-15 18:44:31 +03:00

29 lines
1.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi.testclient import TestClient
from app.main import app
def test_health() -> None:
client = TestClient(app)
response = client.get("/health")
assert response.status_code == 200
body = response.json()
assert body["status"] == "ok"
def test_health_head_ok_no_body() -> None:
"""HEAD /health — то, что реально шлёт внешний uptime-monitor через Caddy
(`handle /health { reverse_proxy backend:8000 }`, Caddyfile:60), не GET.
Starlette не добавляет HEAD автоматически к `@app.get()` (в отличие от
низкоуровневого `Route(methods=["GET"])`) — без явного `@app.head()`
прод-эндпоинт отдаёт 405 на HEAD.
"""
client = TestClient(app)
response = client.head("/health")
assert response.status_code == 200
assert response.content == b""
# RFC 9110 §9.3.2 — заголовки представления (Content-Type) должны совпадать
# с GET; Content-Length допустимо не совпадать (payload header field, MAY
# быть опущен для HEAD).
assert response.headers["content-type"] == "application/json"