Разберите ошибку в интеграционном тесте: сервер возвращает тело для HEAD-запроса. Какое нарушение HTTP-контракта должен выявить тест?
HEAD /reports/42 HTTP/1.1
Host: api.example.test
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 18420
%PDF-1.7 ...
Интеграционный тест должен выявить, что ответ на HEAD содержит тело. По HTTP-контракту сервер должен сформировать те же существенные заголовки, что и для соответствующего GET, но не передавать содержимое представления в теле ответа.
При этом Content-Length: 18420 допустим: он может сообщать размер тела, которое было бы возвращено для GET. Ошибкой является именно фактическая передача байтов после заголовков.
Метод HEAD появился как способ получить метаданные ресурса без загрузки самого представления. Это полезно для проверки существования ресурса, его размера, типа, версии или условий кэширования перед выполнением GET.
Такой механизм снижает лишний сетевой трафик и позволяет клиентам, прокси и системам мониторинга принимать решения по заголовкам. Поэтому отсутствие тела является не оптимизацией конкретного сервера, а частью семантики метода.
Если сервер отправляет тело для HEAD, клиент может ошибочно считать ответ нарушенным, зависнуть в ожидании обработки содержимого или некорректно синхронизировать последующий обмен в постоянном HTTP-соединении. Прокси и кэши также могут по-разному интерпретировать такой ответ.
Проверка только статус-кода 200 не обнаружит дефект. Проверка только Content-Length тоже недостаточна: этот заголовок описывает ожидаемый размер представления, но не доказывает, что тело действительно отсутствует.
Интеграционная проверка должна отправить HEAD на ресурс, для которого известен эквивалентный GET, и отдельно проверить:
GET, например Content-Type, ETag или Last-Modified, если они входят в контракт;Content-Length, если этот заголовок предусмотрен контрактом.Минимальный пример проверки выглядит так:
Сравнивать все заголовки HEAD и GET побайтно не всегда правильно: часть заголовков может зависеть от конкретного запроса, а служебные поля вроде Date изменяются со временем. Нужен явно определённый набор контрактных заголовков.
Тестовый клиент не должен автоматически скрывать нарушение. Если библиотека сама отбрасывает тело ответа на HEAD, следует дополнительно проверять обмен на уровне HTTP-клиента, прокси или использовать инструмент, позволяющий увидеть фактически полученные байты.
Сервис документов корректно отвечал на GET, но после добавления общего обработчика ответов начал сериализовать объект документа и для HEAD. Функциональные тесты проходили, потому что проверяли только статус и заголовки.
Рассматривались два варианта. Можно было проверять только отсутствие полезного содержимого после десериализации, но это не гарантировало отсутствие переданных байтов. Другой вариант — проверять необработанное тело ответа на границе HTTP; он сложнее, зато непосредственно проверяет контракт.
Выбрали второй вариант для интеграционного теста и отдельный unit-тест обработчика. После исправления сервер стал формировать заголовки без записи представления в тело, а тест начал обнаруживать регрессии независимо от содержимого документа.
Должен ли HEAD возвращать те же заголовки, что и GET?
Не требуется механически копировать абсолютно каждый заголовок, но сервер должен сообщать метаданные, которые соответствовали бы обработке эквивалентного GET, если они применимы к ресурсу. Интеграционный тест должен проверять только существенные заголовки, заранее включённые в контракт.
Является ли Content-Length в ответе HEAD ошибкой?
Нет. Этот заголовок может указывать размер тела, которое было бы отправлено при GET. Ошибкой будет несоответствие заявленного размера фактическому размеру GET или наличие самого тела в ответе HEAD, если контракт не предусматривает иное поведение промежуточного компонента.
Можно ли заменить проверку HEAD проверкой GET с условием или диапазоном?
Нет, это проверяет другую семантику. Условный GET может вернуть 304 Not Modified, а запрос с диапазоном — частичное содержимое. Только HEAD позволяет проверить, что сервер предоставляет метаданные ресурса без передачи его представления, поэтому для этого требования нужен отдельный интеграционный тест.