Привет, Хабр! Меня зовут Станислав Кулагин, я ведущий инженер отдела сертификационного тестирования в YADRO. Я разработал ATS Studio — Flask-приложение, которое позволяло запускать автотесты в TestY TMS из браузера, не проставляя статусы руками. За полгода приложение стало популярным в нашей компании теперь экономит по 40 часов в месяц коллегам из KVADRA.
Но я заметил, что у ATS есть потенциал стать лучше, поэтому начал разрабатывать вторую версию. Теперь ATS умеет обрабатывать до 400 тестов одновременно и подходит для совместного использования. В статье расскажу, как появился ATS Framework и почему TestY остается краеугольным камнем этой истории.
Небольшая предыстория
Как я писал выше, приложение ATS Studio работало, а я сравнивал проект с Windows HLK — своим ориентиром в мире сертификационного тестирования. Однако за год стало ясно: прототип, собранный на коленке инженером-тестировщиком, плохо переносит масштабирование на несколько команд и параллельные прогоны.
ATS Studio требовала Secure Shell (SSH) до каждой тестовой машины, ручной настройки через Ansible, и все это было ограничено запуском команд через SSH. Когда несколько человек одновременно использовали ATS Studio, она начинала ломаться. «Дырки» в изолированных подсетях, прокси для TestY и ttyd в браузере решали задачу здесь и сейчас, но тоже быстро исчерпали себя. Хотя бы потому что ИБ не дремлет.
Я по-прежнему не разработчик. Но когда в TestY появились HTTP-интеграции вида http://host:8099/run?project={{TestY\_PROJECT\_ID}}&plan={{TestY\_PLAN\_ID}}, я смог посмотреть на ATS иначе: не как на кнопку запуска скриптов на удаленной машине, а как на API-сервис. За полгода работы Flask-студия превратилась в ATS Framework на FastAPI: появились webhook, очередь прогонов, история, бронирование стендов и установка одним скриптом.
Что еще нового в TestY TMS? В прошлом месяце вышла очередная версия: коллеги доработали представления тест-кейсов, фильтрацию и создание кастомных атрибутов. Узнайте больше можно на странице TestY в GitLab.
Триггером к началу второй итерации разработки стал проект с роботом для автоматизации взаимодействия с BIOS персональных устройств через PiKVM. Команда планирует перенести в TestY до 400 тестов, которые раньше гоняли вручную, и масштабировать стенды до 30 устройств. Регрессы проходили раз в два месяца, и на каждом они целились в экономию до 100 человекодней, которые раньше уходили на ручную работу в BIOS и ручной перенос результатов в Confluence или Kiwi. А тем, кто работал с Kiwi, я настоятельно рекомендую перестать этим заниматься.
Давайте разберемся, что именно сломалось в первой версии и как устроен новый ATS Framework. Главный тезис сейчас: «что угодно как тест-кейс в TestY». Следующий шаг: «что угодно и где угодно как тест-кейс в TestY». Но об этом ближе к финалу.
Раньше всех новости о TestY TMS узнают подписчики рассылки. Оставьте email-адрес на странице подписки — ближайшее письмо придет 10 июня.
Что такое ATS и зачем он нужен
Расскажу в двух словах, если вы не читали первую статью. ATS (Automated Testing System) — мой клиент для TestY, TMS с открытым исходным кодом от компании YADRO. TestY помогает управлять тестированием: есть тест-кейсы, тест-планы, статусы, вложения. ATS берет тестовый план, выполняет привязанные скрипты на машине, сама выставляет статусы и прикрепляет файлы в TestY. Человеку остается создать план и нажать «запустить». Или даже не нажимать, если настроен webhook или CI, например, через Jenkins.
Ключевой идеей приложения с самого начала были файловые маркеры:
Скрипт любого языка в конце создает файл
PASSED_<что-то>— тест прошел.Создал что-то другое — статус
Verify, смотрим сами.Ничего не создал —
Failed.Прервали Ctrl+C —
Interrupted.
Никакой магии, никакой привязки к фреймворку. Скрипт линкуется к тест-кейсу через кастомное поле ATS Executable (путь на машине или команда). Философия простая и до сих пор актуальная: один тест — один скрипт — один результат. Если попадается сложный сценарий из пяти этапов, то создается пять тест-кейсов в одном тест-плане, а не один монолит.
В первой версии ATS вырос из bash в Python, обзавелся Flask-интерфейсом и даже уехал к коллегам из KVADRA. Во второй версии я сознательно не изменял эту философию, а просто переписал все вокруг нее.
Почему ATS Studio исчерпала себя
Когда я писал ATS Studio, задача звучала так: запускать ATS-клиент на удаленных Systems Under Test (SUT) через веб-интерфейс. Flask выступал в качестве сервера, SSH — транспорта, Ansible подготавливал «голую» машину. Ttyd, как терминал в браузере, помогал видеть, как идут тесты, и слать Ctrl+C или input для интерактивных тестов. Squid на сервере Studio — это прокси, чтобы SUT из изолированной подсети достучалась до TestY. Дополнительные сетевые интерфейсы на машине со Studio — те самые «дырки» между подсетями.
Полгода назад это выглядело впечатляюще. Я до сих пор считаю ttyd в отдельной вкладке красивым решением первой версии: создает впечатление, будто сижу прямо на SUT. Это всегда было актуально и важно. Но в эксплуатации каждый слой добавлял точку отказа.
Монолит и тяжеловесность
Flask-сервер, SSH-сессии, генерация inventory для Ansible, копирование клиента на SUT, проверка ОС, скрипт конфигурации, снова проверки, что все живое, прокси-интерфейсы — все в одной связке. Код разрастался, а изменить одно, не задев другое, становилось все сложнее.
Развертывание
Каждая новая машина — мини-проект: настроить SUT, прогнать плейбук, убедиться, что venv на месте, что прокси выбран правильно. Infrastructure as code там был, но для удаленной SUT, а не для самого ATS как продукта.
Прозрачность
Когда прогон идет в ttyd на другой машине, а статусы улетают в TestY асинхронно, сложно понять, что сейчас происходит и кто виноват. История прогонов, очередь, метрики — всего этого не было даже в теории, поскольку чудовище Франкенштейна хоть и ожило, но работало со скрипом.
Несколько пользователей
ATS Studio не рассчитывалась на то, что два инженера одновременно нажмут «запустить» или проведут параллельные сессии.
SSH-only
Главная функциональная стена. Вся модель строилась вокруг выполнения команды по SSH на удаленной машине. Окружение пользователя, графическая сессия, локальное железо — мимо. Нужен тест с окном на мониторе или проверка Bluetooth в пользовательской сессии? Нужен сценарий, где важны DISPLAY, аудио или доступ к GPU? В ATS Studio это было в принципе невозможно, поскольку ATS-клиент жил в SSH-сессии без всякого графического контекста. Для сертификации ноутбуков и рабочих станций — а именно такой профиль и есть самая большая боль — это не мелочь, а один из главных блокеров, который и подтолкнул к переписыванию.
Костыли своего времени
Ansible, ttyd, прокси и «дырки» в сетях не ошибки. Они честно могли запустить приложение из браузера в корпоративной сети с кучей VLAN. Но это были заплатки, а не фундамент. Да и ИБ за такое не похвалит. Когда появился webhook в TestY и нарисовался проект с PiKVM на 30 стендов, стало ясно: тащить это дальше — значит, стрелять себе в ноги и руки. А еще ведь надо было копировать сами файлы скриптов и поддерживать их актуальность. Зачем, если рядом есть BitBucket.
Webhook в TestY и рождение ATS Framework
В первой статье я мечтал о кнопке «запустить тест-план» прямо в TestY. Разработчики TestY идею поддержали, но реализация откладывалась — и я сделал свою кнопку в виде ATS Studio.
Но в TestY появились интеграции — HTTP-вызовы в тест-плане. URL вида:
http://<IP>:8099/run?project={{TestY_PROJECT_ID}}&plan={{TestY_PLAN_ID}}
Этот простой триггер позволил пересмотреть всю модель. TestY перестала быть хранилищем, откуда ATS тянет план по расписанию или по клику в Studio. TMS стала инициатором и работала по принципу «вот план — выполни». Мне нужен был не Flask с формами для SSH и его JavaScript, а стабильный и простой HTTP API, который принимает webhook, и дальше делает с ним то, что нужно мне: ставит прогон в очередь, обрабатывает и отвечает предсказуемо.
Так появился ATS Framework — сервис на FastAPI, который крутится как systemd-unit на хосте с доступом к TestY и BitBucket. Устанавливается в /opt/ats-gui одним скриптом. Никакого SSH до SUT, никаких «дырок». Сознательное упрощение: сначала сделать то, что хорошо работает в центральной инфраструктуре, а изолированные сети вернуть позже — уже на том же FastAPI, без Flask и без прокси-костылей Studio.
Почему не Flask снова?
ATS Studio уперлась не только в архитектуру, но и в границы самого Flask для моих задач. Мне нужны были полноценные API-эндпойнты, очередь, поведение для логов в WEB, HTML-ответы для webhook до начала выполнения тест-плана. Я хотел, чтобы инженер открыл ссылку в браузере и сразу понял, принят прогон или стенд занят.
FastAPI дал все это из коробки. Я получил нормальную работу с async, роутеры по модулям и главное — единый инструмент: API для TestY, WEB для человека, одна кодовая база. С Flask я постоянно уходил в связку «Python + куча JavaScript», и это тоже быстро себя исчерпало.

Когда webhook срабатывает, ATS Framework:
Проверяет
token, если задан.Забирает тест-план из TestY через REST API.
Фильтрует тесты по параметру
skip— например, не трогать тесты со статусомPassed.Проверяет, не заняты ли PiKVM-стенды, если в executable есть
cv-testerс--host.Ставит прогон в очередь и отвечает: JSON для машины или HTML для человека.
Очередь рассчитана на параллельность: до 30 одновременных прогонов, до 50 в очереди. Для регресса на десятках стендов это уже не «скрипт в терминале», а сервис. Это все тоже FastAPI умеет сам.
Ключевой момент: общение между TestY и ATS идет только через API и WEB. Никаких «открой Studio, введи IP, нажми настроить». Тестировщик работает в TestY или дергает webhook из браузера. FastAPI здесь не заменяет Flask ради моды, это решение, которое за полгода работы реально собрало все в одно целое.
TestY не архив тестов, а платформа
За время работы с TestY я окончательно убедился, что ее сила не в еще одном списке тест-кейсов, а в гибкости модели данных. Именно она позволяет любую идею для моих нужд добавить на лету. ATS Framework остается универсальным исполнителем, а не монолитом под один проект.
Кастомные поля
TestY позволяет теперь добавлять поля разных типов (select, multiselect, JSON и другие) на уровне проекта и фильтровать, где поле будет отображаться (тест-кейс, тест-план, результат) и при каких условиях. Для простых автотестов достаточно ATS Executable, то есть одной строки с командой. Для сложных сценариев с роботом BIOS JSON-поля хранят параметры, конфиги и метаданные, которые ATS или сам тест читают из API. Не нужно плодить отдельные репозитории конфигов — все живет рядом с тест-кейсом в TMS. Получили что нужно — добавили в TestY куда нужно. Profit!
Кастомные статусы
В ATS Framework добавились статусы Warning и Broken. TestY позволяет завести статус с любым именем и цветом. Теперь при первичной настройке через WEB Configure ID статусов синхронизируются с TestY, TSM сохраняет их в зашифрованный конфиг. В TestY видна очередь: что в ожидании, что выполняется прямо сейчас.
Метки и фильтрация
Теги вроде Autotest или User GUI работают по тому же принципу. Поля и статусы можно ограничивать сущностями: только для определенных тест-планов, только для результатов, только при заданных метках. Одна ветка проекта в TestY содержит разные команды и сценарии, без форков TMS и даже без отдельной ветки проекта TestY на каждый чих.
Webhook с плейсхолдерами
{{TestY_PROJECT_ID}} и {{TestY_PLAN_ID}} подставляются TestY автоматически. Интеграция привязана к тест-плану. ATS всегда знает, что запускать, а вводить ID в форме вручную не нужно.

Бизнес-логика «что, когда и с какими параметрами гонять» описывается в TMS инженерами-тестировщиками в несколько кликов. ATS читает план через API и делает то, что написано в полях и executable ровно в том порядке, какой нужен. Хотите завтра гонять bash, послезавтра — Python с GPU, через месяц — робота BIOS в Podman? Меняете тест-кейс в TestY, а не переписываете ATS.
«Что угодно как тест-кейс» в TestY
Главный тезис ATS Framework простой: если это можно описать в TestY как тест-кейс, ATS может это выполнить. Не только bash из /opt/tests, не только наш фреймворк, не только то, что влезло в SSH-сессию. Заполняйте команды, executable, метки, JSON-поля, и дальше ATS разбирается сам.
Bash, Python и маркеры — наследие, которое не трогали
Философия файловых маркеров из первой статьи осталась без изменений, но эвристика стала богаче и теперь покрывает гораздо больше ситуаций. Вкратце:
Скрипт создал
PASSED_*—Passed.Создал
WARNING_*—Warning. Для случаев, когдаFailedне подходит.Явный
FAILED_*—Failed. Ничего из этого — тожеFailed, потому что тест отработал, но успех не заявил.Непонятные файлы —
Verify. А если их много – то архив из них.Прервали —
Interrupted.Словили exception –
Broken.
Раньше ATS была «умной в одну строку». Сейчас она различает ситуации, которые в отчете нельзя смешивать:
Failed— тест доехал до конца, но результат отрицательный. Скрипт не упал, просто не создал маркер успеха. Или создалFAILED_*. Это баг продукта или ошибка сценария.Broken— сломалась инфраструктура или выполнение. Exception в скрипте или ненулевой exit code без явного FAILED-маркера (все exit code теперь тоже попадают в результат TestY). Нет Podman-образа или битый JSON в отчете робота BIOS.
Разница на практике:
# Failed: скрипт отработал штатно, но не заявил об успехе time.sleep(5) # нет PASSED_* → ATS ставит Failed
# Broken: упал рантайм time.sleep(5) raise RuntimeError("…") # → ATS ставит Broken
На регрессе из сотен тестов это критично. Если упал тест BIOS и одновременно произошел exception на старте — это нехорошо.
Warning оказался полезен для некоторых сценариев — тест формально прошел, но с оговорками (WARNING_*-маркер, логика зашита в сам тест). И Verify — когда ATS нашла файлы или скриншоты, но однозначного маркера нет: несколько артефактов упаковываются в ZIP, человек смотрит в TestY.

Робот BIOS, Podman и одна строка в TestY
Самый показательный кейс — проект с роботом для автоматизации BIOS через PiKVM.
У ноутбуков нет нормального интерфейса для автотестов BIOS, либо они стоят безумно дорого. Используя PiKVM, мы получаем картинку с экрана, к которому подключен Device Under Test (DUT). Робот смотрит на изображение через OpenCV или Tesseract, имитирует нажатия, строит карту меню и проходит сценарии через YAML или Python. Раньше пять человек почти две недели проверяли пять устройств вручную, потом переносили логи и скриншоты в Confluence или Kiwi. Работа, которую не пожелаешь и врагу. С ATS Framework цепочка выглядит иначе. Автотестеры пишут сценарий для робота, проверяют локально. Добавляют в TestY одну строку в поле ATS Executable:
uv run cv-tester --host 172.17.x.x -d py --script test_demo
И всё. Что делает ATS Framework:
распознает, что executable — это cv-tester;
оборачивает запуск в Podman-контейнер, который собирается из Bitbucket (cv-tester:ats);
монтирует уникальный каталог bind mount для артефактов;
парсит JSON-отчет робота и маппит PASS / FAIL / WARNING / ERROR в статусы TestY;
прикрепляет скриншоты и логи прямо в результат без ручного копирования.
Тестировщик ничего не знает про контейнеры, bind mount и --network host. Он описал команду в TestY, ATS взяла инфраструктуру на себя.

Образ и исходники подтягиваются через WEB: логин Bitbucket, ссылка на репозиторий, sync, build. Контейнеров раньше вообще не было, с прицелом на 30 стендов это стало необходимо.
Масштаб амбициозный: до 400 тестов в TestY, до 30 PiKVM-стендов, параллельные прогоны. Регрессы — каждые два месяца, и на каждом мы целимся сэкономить до 100 человекодней ручной работы. Тестировщики дергают webhook из TestY или из WEB ATS и идут заниматься тем, что не автоматизируется за одну строку ATS Executable. Сюда же прикручиваются джобы Jenkins. И пока идут выходные, ATS гоняет робота, который гоняет BIOS.
Графическое окружение: то, что SSH не умел
В ATS Studio я упирался в стену раньше, чем тест успевал стартовать. Все выполнение, как я уже писал, шло через SSH на удаленную машину. Команда выполнялась в чужой headless-сессии. Для сценариев «как пользователь за ноутбуком» это тупик.
ATS Framework можно поставить на хост с графическим окружением Linux или без него — на Ubuntu, Astra Linux, ALT Linux, РЕД ОС, СберLinux. При настройке через WEB Configure сервис подхватывает переменные активной пользовательской сессии, если они есть: DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR. Configure показывает, нашелся ли DISPLAY, появляется зеленый бейдж или предупреждение.
Тесты с меткой User в TestY запускаются от обычного пользователя. Superuser — через sudo, если сценарий лезет туда, куда без root нельзя. В итоге получается, что glxgears или clock реально открывает окно на несколько секунд, есть поддержка пользовательских проверок.
Для текущего проекта это закрывает одну из главных проблем прошлой версии. Нужен тест с выводом на подключенный монитор? Запускается в пользовательской сессии с DISPLAY. Нужна проверка Bluetooth-адаптера или другого железа, доступного только из desktop-окружения? ATS Framework живет на той же машине, где сидит пользователь и где физически DUT.
ATS Studio была своеобразным пультом для удаленных SUT через SSH. ATS Framework теперь может быть исполнителем на рабочей станции или выделенном ATS-хосте с GUI. Сертификация персональных устройств требует user session. SSH-only-модель этого не давала.
Бронирование стендов: ATS стала умнее
Когда прогонов много и PiKVM 30 штук, запуск чего угодно быстро превращается в убийство чужого стенда. Два webhook на один --host — и два робота лезут на один DUT. Проблема.
ATS Framework перед принятием прогона парсит --host из executable cv-tester и бронирует PiKVM в SQLite. Транзакция: acquire → выполнение → release. Если стенд занят, webhook отвечает HTTP 409 Conflict с понятной HTML-страницей. Больше она на чужое железо не позарится.


В БД пишется audit. Вкладка Stands в WEB обновляется без копания в логах в прямом эфире. Health dashboard следит за зависшими бронированиями.
ATS перестала быть скриптом, который гоняет команды. Она обзавелась состоянием: очередь, история в SQLite, блокировки стендов, зашифрованный конфиг. Preflight перед webhook проверяет --host, наличие образа Podman, размер runlist после skip.
WEB вместо ttyd: лог, stdin и SIGINT
В ATS Studio был терминал в браузере через ttyd. Но тогда это был единственный доступный костыль. На этапе фреймворка ttyd стал бутылочным горлышком: жил на другой машине, зависел от SSH и не знал бы про очередь и историю.
Сейчас FastAPI сам отдает лог выполнения в WEB. На странице деталей прогона в History есть live-статус, Ats queued / Ats running, длительность, маркеры.

Интерактивность сохранилась: stdin подходит для running-прогона, кнопка SIGINT работает как Ctrl+C. Можно прервать весь тест-план, текущий тест или вообще все running-прогоны.
Очередь поддерживает до 30 прогонов параллельно и до 50 в ожидании. Webhook отвечает красивым HTML. Ttyd полгода назад сделал Studio живой. ATS Framework дает тот же смысл на архитектуре с параллельностью без костыля.
History, Health и шифрование конфига
History
Сохраняется каждый webhook-прогон в SQLite и HTML-лог, время выполнения, детали тест-плана, маркеры. Можно открыть через год и увидеть тот же поток, что был, в живом выводе.
Health
Systemd, креды TestY, DISPLAY, образ cv-tester, логи, зависшие стенды, потребляемые ресурсы — все это проходит диагностику на одно экране.

Шифрование state.json
В Studio логины и пароли TestY передавались параметрами CLI и формами. Это работало, пока инструментом пользовался один человек. Когда ATS Framework стал сервисом, на котором могут крутиться разные прогоны, хранить креды открытым текстом на диске стало некомфортно.
Сейчас я применил state.json, и ID проекта, синхронизированные ID статусов TestY, пароли TestY, Bitbucket, sudo сохраняются в зашифрованном виде. Configure вызываю один раз: ввел данные в WEB, нажал «Применить» — сервис перезапустился, секреты лежат в конфиге, а не в истории bash. К тому же теперь для ATS Framework своя учетная запись для TestY. Начальная гигиена информационной безопасности.
Установка за минуту, self-tests и MLP
ATS Studio разворачивалась кусками: сервер здесь, SUT там, Ansible, venv на удаленке, прокси, ttyd. ATS Framework ставится на голую машину скриптом install_ats.sh --auto — Ubuntu, Astra, ALT, РЕД ОС, СберLinux. В авторежиме у меня выходит около минуты. За это время подгружаются venv, зависимости, systemd-unit, каталоги для баз данных и логов. Дальше в WEB Configure автоматически загружаются креды TestY, sudo, project ID и синхронизация кастомных статусов. После этого webhook уже можно дергать. Дальше скачать нужный репозиторий с тестами из Bitbucket — и все уже поехало, выбирай тест-план.
Также в саму ATS я добавил 30 встроенных тестов — сценарии на bash и Python для проверки валидности маркеров Passed/Failed/Warning, Verify с ZIP, raw data в JSON, inline-картинки в результат TestY, интерактивный stdin, Broken на Exception, Failed без маркера, GPU smoke в user session. Регресс эвристики как он есть.

Я узнал модную аббревиатуру MLP — minimum lovable product. Как я понял, это не еще одна итерация на коленке, а готовый, отлаженный продукт, которым не стыдно пользоваться и который можно нести в другие команды.
Планы на будущее: «что угодно и где угодно»
В первой статье я мечтал запускать тесты откуда угодно: из изолированных подсетей, прокси, SSH до SUT. ATS Studio это частично дала, но ценой «дырок» в VLAN и тяжелого деплоя с костылями в обеих руках.
Сейчас я сознательно упростил задачу: ATS Framework живет на хосте, откуда есть доступ к TestY и Bitbucket, и общается с TMS напрямую. Модель «что угодно как тест-кейс» не изменилась. Bash, Python, GPU smoke, робот BIOS в контейнере, обычные shell-команды — все работает через поля TestY и один webhook.
Следующий шаг — «где угодно»: вернуть работу в изолированных сетях, но уже на том же FastAPI, без Flask, без Ansible на каждую SUT, без ttyd. Хочется не откатываться назад к Studio, а положить второй слой поверх отлаженного API после того, как текущий проект с PiKVM уедет в прод и мы соберем свои плюшки в виде человекодней.
ATS Framework научился тому, чем Studio не могла стать: появились графическое окружение, бронирование стендов, история, шифрование, очередь, Podman из коробки. И все это за полгода.
Если вы тоже устали кликать статусы в TMS руками — посмотрите на свой REST API. Возможно, вам тоже не нужна команда разработки. А нужен webhook, немного FastAPI и TMS, которая не мешает, а подстраивается — как TestY.
Задавайте в комментариях вопросы о разработке ATS Framework — на них отвечу я. А на вопросы о TestY TMS — разработчики системы.