Серия: агент против легаси. Защитная сетка из тестов

Третий пост серии. После карты модуля и security-дыр, которые ловятся чтением кода, сделали аудит текущих тестов и дописали поведенческие тесты: описали не «правильное» поведение, а покрыли то, что код делает прямо сейчас, баги включительно. Из 73 существующих тестов добили до 130 тестов, продовый код не тронут, 12 дефектов зафиксированы отдельной меткой.

На «обычной» логике тесты не нашли ничего нового. Контроллеры, роли, авторизация — 38 тестов написаны, все прошли с первого раза. То есть всё, что можно было найти по коду, было найдено на прошлых этапах. Тесты здесь просто подтвердили разбор.

А вот на слое ORM всё было иначе. DELETE /cart/{id} отвечает 200, корзина в ответе пустеет — выглядит так, будто удаление сработало. Три гипотезы теста подряд проваливаются, и вскрывается: строка навсегда остаётся в product_in_order, просто с cart_id = NULL. Ни ручной разбор (Opus, по всему модулю), ни четыре параллельных субагента GSD этот дефект не поймали — потому что он не написан в коде ни в каком виде. Это поведение persistence context в момент flush, а не логика, которую можно прочитать. Три разных метода — чтение человеком, параллельные агенты, тесты — поймали три непересекающихся класса дефектов. Ни один по отдельности не покрывает всё.

Второй кейс интереснее не сам по себе, а тем, что показывает, как приоритизация по списку теряет риск. По отдельности в списке находок значились: GET /profile отдаёт клиенту его собственный BCrypt-хэш, и update() безусловно перехэшировает пришедшее значение. Оба — замечания среднего приоритета, ничего критичного. Тест собрал их вместе: обычный round-trip «прочитал профиль → поменял имя → отправил обратно» меняет пароль пользователя на строку, которую он никогда не вводил. Он просто перестаёт мочь зайти в свой аккаунт. Списком находок эта связь не видна — только когда прогоняешь сценарий целиком.

И отдельно про процесс, а не про баги. Перед прогоном интеграционных тестов агент сам проверил docker ps — и нашёл на localhost:5432 базу совсем другого моего проекта, которую ddl-auto: create уничтожил бы при обычном запуске. Не повезло, а сработал процесс: агент проверил окружение, а не понадеялся на него.

Вывод. Список найденных проблем — не то же самое, что список рисков: связи между пунктами теряются, если не проиграть сценарий целиком. И баги на уровне ORM/persistence — отдельный класс дефектов, невидимый ни для человека, ни для агента, пока код не запущен. Дальше — миграция на Spring Boot 3, и там нашлось кое-что похуже отдельного бага.