Привет, Хабр! Меня зовут Станислав Кулагин, я ведущий инженер отдела сертификационного тестирования в 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:

  1. Проверяет token, если задан.

  2. Забирает тест-план из TestY через REST API.

  3. Фильтрует тесты по параметру skip — например, не трогать тесты со статусом Passed.

  4. Проверяет, не заняты ли PiKVM-стенды, если в executable есть cv-tester с --host.

  5. Ставит прогон в очередь и отвечает: 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 в форме вручную не нужно.

Вся «что запускать» живёт в Testy. ATS Framework только исполняет
Вся «что запускать» живёт в Testy. ATS Framework только исполняет

Бизнес-логика «что, когда и с какими параметрами гонять» описывается в 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. В TestY остается одна строка ATS Executable
Репозиторий и образ — через WEB. В TestY остается одна строка ATS Executable

Образ и исходники подтягиваются через 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, длительность, маркеры.

Вместо ttyd на SUT — лог в ATS Framework. Плюс много деталей
Вместо ttyd на SUT — лог в ATS Framework. Плюс много деталей

Интерактивность сохранилась: 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, логи, зависшие стенды, потребляемые ресурсы — все это проходит диагностику на одно экране.

Диагностика одним экраном — в Studio такого не было и не планировалось
Диагностика одним экраном — в Studio такого не было и не планировалось

Шифрование 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. Регресс эвристики как он есть.

 Результат в TestY со скриншотами и json-данными
Результат в TestY со скриншотами и json-данными

Я узнал модную аббревиатуру 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 — разработчики системы.

Комментарии (0)