Как река возрастом 2000 лет научила меня стабилизировать наш сложнейший тест — почти не тратя времени

Опубликовано  · Игорь Ткаченко

Вечерняя сцена, объединяющая древний китайский кабинет с современным рабочим столом разработчика — счастливый основатель и мультяшные совы OwlMeans в китайских халатах ухаживают за моделью реки с рыбьим ртом и светящимся кубом Kubernetes; заголовок: IT RUNS ITSELF

В 256 году до нашей эры губернатор по имени Ли Бин столкнулся с рекой, которая каждую весну выходила из берегов и топила урожай. Очевидным решением была дамба. Он её не построил.

Он разделил реку Минь каменной дамбой в форме рыбьего рта и позволил разделению самому делать работу — большая часть потока на поля в сухие месяцы, избыток сбрасывается обратно в паводок, ил уходит вместе с ним. Никаких шлюзов, никакого персонала. Река регулировала себя сама и до сих пор орошает равнину Чэнду — двадцать три века спустя.

В этом месяце я много думал о Ли Бине. Мой сложнейший тест имел болезнь реки: он выходил из берегов при каждом запуске.

Поток, который нельзя подделать

OwlMeans — это AI-пайплайн разработки: описываешь продукт в виде пользовательских историй, а специализированные AI-роли превращают их в полноценное приложение, которое принадлежит тебе. Чтобы протестировать это end-to-end, нельзя использовать моки — запускаешь реальное.

Тест регистрирует пользователя, вводит промпт, ждёт спецификацию, подтверждает её — и тут начинается самая сложная часть: для этого проекта поднимается новый sandbox Kubernetes, агент разрабатывает первую историю внутри него, и тест ждёт, пока история не будет завершена. От десяти до сорока минут, все возможные асинхронные операции между — и все возможные причины для нестабильности. Остаточное состояние, наполовину развёрнутый sandbox, агент, который завис, интерфейс, меняющийся на глазах. Наивная версия — это восьмидесятиминутный таймаут и молитва.

Половина, которую я не писал

Первая часть трюка: большую часть этого я не строил.

Браузерная сторона работает на нашем собственном харнесе, @owlmeans/test-ui — одной из общих библиотек OwlMeans, на которых построен каждый проект здесь. Он запускает браузер, входит через наши ключи-супервайзеры вместо реального входа через Google и оборачивает Playwright-сантехнику так, что мой тест читается как описание намерений.

Sandbox тоже не требовал моего участия. Эфемерный Kubernetes-слот — namespace, storage, deployment, сетевая политика — провизионируется и поддерживается живым другой общей библиотекой, @owlmeans/kephemeral: она наблюдает за podом и лечит его, если он упадёт. Она уже знала, как держаться.

Остался один поток для укрощения: как зависеть от потока, который реально занимает сорок минут, не следя за ним?

Рыбий рот

Нельзя использовать короткий таймаут — агент реально может думать сорок минут, и это нормально. Также нельзя ждать восемьдесят минут, чтобы узнать, что он умер на третьей. Фиксированный таймер — это дамба: слишком низкий — затопляет, слишком высокий — бесполезный.

Поэтому я перестал измерять время потока и начал читать его пульс. Агент расходует токены во время работы — это не прокси прогресса, это и есть прогресс. Тест наблюдает за балансом токенов. Каждые двадцать секунд: всё ещё падает? Если да — агент жив, жди сколько нужно. Если остановилось на десять минут — агент завис, и тест падает прямо сейчас, а не смотрит в стену ещё час.

Вот и вся идея: собственное поведение потока говорит мне, здоров ли он, так же как течение говорит дамбе Ли Бина, сколько пропустить. Тридцать строк кода превратили мой самый нестабильный тест в самый надёжный.

Чистый лист на вход, чистый лист на выход

Ещё одна утечка. Запуск, прерванный на полпути, оставляет за собой целый sandbox — deployment, storage, сертификаты, маршруты, namespace — и следующий запуск спотыкается об этот мусор и не хочет стартовать.

Исправление скучное и тотальное: перед каждым запуском и после него тест уничтожает каждый проект на одном повторно используемом профиле. Как бы предыдущий запуск ни закончился — чистый выход, краш, закрытый ноутбук — следующий стартует с известного, пустого состояния. Никакой ручной уборки, никогда — чистый лист на каждый запуск — это лучшее, что можно сделать против нестабильных тестов, и здесь это бесплатно.

Акведук

Облачная иконка Cloudflare в левом верхнем углу соединяется вниз через relay-сервер, каменную арку туннеля (зашифрованный туннель WireGuard) и маленький домик — домашний сервер — к светящемуся кубу с гербом Kubernetes; сова OwlMeans наблюдает из угла

Осталась одна проблема — та, которую большинство так и не решает чисто: кластер работает на сервере дома — без публичного IP, за провайдером, который не принимает входящий трафик. Чтобы тестировать его откуда угодно и поставить перед ним Cloudflare, мне нужно было вынести поток из дома.

Это отдельный маленький проект, flt-pf-tnl: дешёвая облачная VM с одним зарезервированным статическим IP, запускающая WireGuard и Nginx-прокси. Путь прост — Cloudflare → relay → зашифрованный туннель → домашний сервер → sandbox Kubernetes. Две команды его запускают — только Terraform, Ansible и несколько shell-скриптов. Watchdog каждые шестьдесят секунд проверяет файрвол и исправляет дрейф; статический IP переживает сносы, поэтому публичный адрес никогда не меняется.

Настроено один раз. Держится. Акведук: построен однажды, работает на гравитации.

Можно просто это иметь

Отступи назад. Ничего из этого не является экзотическим — самолечащийся sandbox, обратная связь, читающая собственный пульс потока, туннель, выносящий приватный кластер в мир. Несколько дней правильной структуры вокруг потока, который съедал мои вечера. Эта структура и есть продуктом — то же самое обещание, которое OwlMeans даёт каждому приложению, которое отправляет: типизированное монорепо и инфраструктура вокруг него, твои навсегда.

Поэтому вот честный питч.

Если ты строишь что-то реальное — ты наткнёшься на эту стену: медленные потоки, внешние зависимости, sandbox, который нужно поднять и снести, тесты, которые проходят в понедельник и падают во вторник. Можно делать то, что делал я: сжечь недели, узнавая, что более длинный таймаут никогда не лечит нестабильный поток, что нужно читать его пульс, что доступ к собственному кластеру требует акведука. Для меня это было того стоит; это моя работа.

Это не твоя работа.

Это часть OwlMeans Services: мы берём этот харнес, sandbox и туннель, адаптируем их к твоему стеку и разворачиваем за несколько дней. Всё принадлежит тебе — код, инфраструктура, никакого vendor lock-in, поддерживается любым агентом для кодирования.

Поток, который запускается сам, — так же как река течёт уже двадцать три века. Пропусти месяц, который потратил я — можно просто это иметь.


OwlMeans строит полностековые TypeScript SaaS-приложения из ваших пользовательских историй — типизированные, готовые к SSO, ваши для дальнейшего развития с любым агентом. Посмотрите, на что он способен →