Предложение по автоматизации Devshard E2E тестов
Оригинал: Devshard E2E Test Automation Proposal

@aikuznetsov (https://github.com/aikuznetsov) Пожалуйста, взгляните на это: https://github.com/a-kuprin/gonka/blob/1f0933ad9136cfbcf7070f8210e2c6694731ebaf/devshard/docs/proposals/TESTENV_PROPOSAL.md
Он использует несколько докеров devshardd, 1 devshardctl, 1 dapi-mock и 1 Mock-chain и не использует цепочку.
Он даже уже использовался для тестирования нового протокола синхронизации высоты для devshard: #1209 (https://github.com/gonka-ai/gonka/pull/1209)
Разница в том, что на самом деле используется децентрализованное API (но это имитация протокола). decentrized-api — это MLServer — обслуживающие узлы, а также оракул для параметров и высоты.
Также у меня были некоторые мысли о более высокоуровневых сценариях вместо тестовой среды для создания планов тестирования: https://github.com/a-kuprin/gonka/blob/devshard-testenv/devshard/docs/proposals/PROTOCOL_TESTING_PROPOSAL.md

Цель
Создайте настоящий уровень интеграционного тестирования для devshard, который запускается на основе тестов Go, но проверяет систему с помощью контейнеров Docker, реальной сети HTTP, реальных границ процессов и реального хранилища.
Этот пакет должен дополнять существующие модульные, пакетные и httptest-тесты. Эти тесты остаются слоем быстрой корректности. Пакет E2E проверяет, что один и тот же протокол работает при запуске, подключении, перезапуске и сбое компонентов, как и реальные службы.
Область применения
Тест-раннер — Go. Среда выполнения — Docker.
Пакет не должен зависеть от действующей цепочки Cosmos, Testermint или decentralized-api. Метаданные, связанные с цепочкой, обслуживаются локальным фиктивным сервисом. Для вывода и проверки используются детерминированные механизмы-заглушки, если в сценарии явно не выбран другой бэкэнд.
За пределами первой версии:
проверка стека наблюдения за добычей
долговременная производительность или тестирование на выдержку
подача расчетов в живой цепочке
реальное выполнение модели машинного обучения
полный поток управления версиями
Их можно будет добавить позже как отдельные профили, как только уровень основного протокола E2E станет стабильным.
Инструменты и платформы тестирования
Первая реализация E2E должна сохранить небольшой набор инструментов и использовать Go-native.
Рекомендуемые инструменты:
Инструменты, которых следует избегать в первой версии:
- Исполнители сценариев Python: Go сохраняет сценарии близкими к типам devshard, помощникам подписи, помощникам хранилища и существующим утверждениям. Добавление Python создаст вторую среду выполнения тестов до того, как контракт E2E станет стабильным.
- Docker Compose в качестве основного оркестратора тестов: testcontainers-go дает каждому тесту Go прямой контроль над сетями, контейнерами, портами, журналами, перезапусками и очисткой. Compose может пригодиться позже для ручного воспроизведения.
- живая цепочка Cosmos или Testermint: первый пакет должен изолировать протокол devshard и поведение транспорта от запуска цепочки, производства блоков, управления и несвязанных сбоев узлов. Mock-chain охватывает мостовой контракт, необходимый devshard.
- Реальное выполнение модели машинного обучения: детерминированный вывод заглушки обеспечивает быстроту, воспроизводимость тестов и сосредоточенность на поведении протокола, а не на доступности графического процессора/модели или качестве генерации.
- автоматизация браузера/UI: devshard E2E проверяет HTTP API и состояние протокола. Автоматизация браузера добавит проблемы с медленным пользовательским интерфейсом, которые не являются частью этого предложения.
Структура тестовой среды
Каждый тест запускает изолированную сеть Docker. Процесс тестирования Go остается вне сети и контролирует среду через API-интерфейсы Docker и сопоставленные сервисные порты.
Среда дыма по умолчанию должна развернуться:
один контейнер макетной цепочки
три контейнера devshard-host-N
один контейнер devshardctl
один контейнер Postgres
Сценарии хранения и сбоя добавляют контейнеры или тома по мере необходимости:
постоянные тома SQLite для тестов перезапуска
дополнительные конечные точки управления для каждой службы для детерминированного внедрения ошибок
блок-схема LR TestRunner["Go E2E test runner"] Docker["Docker / testcontainers-go"] Client["HTTP утверждения"] subgraph Net["изолированная сеть Docker"] MockChain["mock-chain\nchain метаданные + API управления"] DevshardCtl["devshardctl\nOpenAI-совместимый API"] Host0["devshard-host-0\nslot 0"] Host1["devshard-host-1\nslot 1"] Host2["devshard-host-2\nslot 2"] Postgres["postgres\nsmoke Storage Backend"] Vol0[("host-0 SQLite Volume")] Vol1[("host-1 SQLite Volume")] Vol2[("host-2 SQLite Volume")] end TestRunner --> Docker TestRunner --> Client Client --> Клиент DevshardCtl -.прямые проверки протокола.-> Клиент Host0 -.прямые проверки протокола.-> Клиент Host1 -.прямые проверки протокола.-> Host2 DevshardCtl --> Host0 DevshardCtl --> Host1 DevshardCtl --> Host2 Host0 <-->|gossip| Хост1 Хост1 <-->|сплетни| Хост2 Хост2 <-->|сплетни| Host0 Host0 --> MockChain Host1 --> MockChain Host2 --> MockChain DevshardCtl --> MockChain Host0 --> Postgres Host1 --> Postgres Host2 --> Postgres Host0 -.sqlite профиль.-> Vol0 Host1 -.sqlite профиль.-> Vol1 Host2 -.sqlite профиль.-> Vol2
Инвентаризация контейнеров:
Первая реализация должна быть стандартизирована для группы из трех хостов, поскольку многие варианты поведения протокола требуют формы, подобной большинству: ротация исполнителей, голосование по тайм-ауту, накопление подписей и сходимость слухов. Жгут может позже подвергать Hosts: N стресс-тестам или тестам в крайних случаях.
Службы времени выполнения
Каждая среда E2E запускает изолированную сеть Docker и небольшой набор сервисов.
макет цепи
Mock-chain — это локальная служба метаданных, которая реализует подмножество поведения моста основной сети, необходимое для devshard.
Первая реализация должна точно соответствовать текущей форме моста REST. Это заставляет E2E сосредоточиться на проверке мостового контракта, который разработчик уже использует, вместо добавления второго API, предназначенного только для макетов. Более чистый API внутреннего контроля можно добавить позже вместе с REST-совместимыми конечными точками, но настройка и восстановление протокола должны продолжать выполняться по тем же путям, что и рабочий код.
Он служит детерминированной локальной конфигурацией для:
идентификатор условного депонирования
адрес создателя условного депонирования
баланс условного депонирования
идентификатор эпохи
хэш приложения
назначение слотов хоста
URL-адреса вывода хоста
цена токена
порог проверки
гранты «теплый ключ»
утвержденные версии devshard, когда они нужны сценарию версии
Он также должен предоставить API управления только для разработчиков для тестовых сценариев:
передовая эпоха
изменить утвержденные версии
изменить метаданные хоста
добавить или удалить гранты «теплых ключей»
ввести задержки ответа
вводить ошибки моста
devshard-host-N
В каждом хост-контейнере работает один участник. Процесс должен использовать реальный хост devshard, транспорт, подпись, хранилище, сплетни и код конечного автомата.
Конфигурируемые входы:
идентификатор условного депонирования
ключ подписывающего лица хоста
адрес пользователя
назначение слота
префикс маршрута
URL-адреса одноранговых хостов
серверная часть хранилища
URL-адрес макетной цепочки
поведение заглушки вывода
поведение проверки заглушки
Хост должен предоставлять стандартные транспортные маршруты devshard, смонтированные либо с префиксом устаревшего маршрута, либо с префиксом версии:
/v1/devshard/* /devshard/<версия>/*
devshardctl
Пакет должен включать сценарии, которые направляют запросы через OpenAI-совместимую поверхность devshardctl. Это подтверждает путь, обращенный к пользователю:
клиент -> devshardctl -> транспортные клиенты devshard -> хост-контейнеры
Некоторые сценарии более низкого уровня могут напрямую обращаться к конечным точкам транспорта хоста, когда это делает утверждение более ясным, но пакет дыма должен использовать devshardctl .
постгрес
Postgres является частью дымовой среды и должен быть базовым хранилищем по умолчанию для дымовых тестов CI. SQLite остается полезным для локальных тестов перезапуска и крайних случаев сохранения состояния одного хоста.
Сценарии хранения должны охватывать:
Перезапуск хоста SQLite
Перезапуск хоста Postgres
перезапуск всех хостов
конфликт версий сеанса
конфликт эпохи сеанса, где это применимо
Тестовые двоичные файлы
Пакету E2E нужны исполняемые команды, которые представляют собой небольшие оболочки существующих пакетов devshard.
Рекомендуемые команды:
devshard/cmd/devshardd/main.go devshard/cmd/mock-chain/main.go
девшардд
devshardd запускает одного хост-участника.
Для первой реализации E2E devshardd должна быть командой только для E2E. Его пока не следует рассматривать как производственный двоичный файл. Это позволяет сосредоточить первую итерацию на проверке интеграции, оставляя при этом место для усиления и продвижения команды позже, если она примет правильную производственную форму.
Он должен подключить:
мостовой клиент
государственная машина
хозяин
транспортный сервер
хранение
коллеги по сплетням
машина вывода
механизм проверки
конечная точка готовности
конечная точка управления только для разработчиков, если она явно включена
Для E2E devshardd может начать с механизмов вывода и проверки заглушки. Важным моментом является то, что сама среда выполнения протокола реальна.
макет цепи
Mock-chain обслуживает локальные метаданные и детерминированное поведение управления. Он должен начинаться как простой HTTP-сервер, соответствующий текущей форме моста REST. Если позже devshard перейдет на другой протокол клиента цепочки, макет должен следовать этой границе.
Внесение ошибок
Детерминированное внесение ошибок должно быть частью проекта тестирования с самого начала. Без него тесты тайм-аута и восстановления становятся медленными и нестабильными.
Первая поверхность управления должна поддерживать:
провалить следующий вывод
отложить следующий вывод
отложить следующий вывод до его отмены
удержать расписку исполнителя
вернуть поврежденный хэш ответа
вернуть неверный результат проверки
приостановить сплетни
возобновить сплетни
отклонять запросы метаданных моста
вернуть устаревшие метаданные моста
предварительная имитация эпохи
изменить утвержденные версии
Средства контроля неисправностей должны быть отключены, если процесс не запущен в явном тестовом режиме.
Набор сценариев
Сценарии дыма
Сценарии дыма должны быть надежными и достаточно быстрыми для каждого запуска CI.
- Счастливый путь Запустите три хоста и devshardctl. Отправьте несколько запросов на завершение непотокового чата. Завершите сеанс. Подтвердите, что выходные данные расчета присутствуют и все хосты согласны с окончательным состоянием.
Счастливый путь
Запустите три хоста и devshardctl. Отправьте несколько запросов на завершение непотокового чата. Завершите сеанс. Подтвердите, что выходные данные расчета присутствуют и все хосты согласны с окончательным состоянием.
- Путь потоковой передачи Отправьте запрос на завершение потокового чата через devshardctl. Подтвердите, что клиент получил фрагменты контента и [DONE] . Получение/мета-события протокола Assert devshard обрабатываются внутри и не повреждают поток, совместимый с OpenAI.
Путь потоковой передачи
Отправьте запрос на завершение потокового чата через devshardctl. Подтвердите, что клиент получил фрагменты контента и [DONE] . Получение/мета-события протокола Assert devshard обрабатываются внутри и не повреждают поток, совместимый с OpenAI.
- Отказ в аутентификации Отправьте запрос защищенного хоста, подписанный неавторизованным ключом. Укажите, что запрос отклонен из-за ошибки авторизации.
Отказ в аутентификации
Отправьте запрос защищенного хоста, подписанный неавторизованным ключом. Укажите, что запрос отклонен из-за ошибки авторизации.
Сценарии протоколов
- Конвергенция сплетен Отправьте работу, пока все хосты работают. Данные подтверждения nonce, мемпула и подписи распространяются между участниками и сходятся.
Сближение сплетен
Отправляйте работу, пока все хосты работают. Данные подтверждения nonce, мемпула и подписи распространяются между участниками и сходятся.
- Догон хоста. Позвольте одному хосту пропустить более ранние различия, а затем отправьте ему более поздний запрос с догоняющими различиями. Утвердите, что он достигает того же корня состояния, что и остальная часть группы.
Догонялка хоста
Позвольте одному хосту пропустить более ранние различия, а затем отправьте ему более поздний запрос с догоняющими различиями. Утвердите, что он достигает того же корня состояния, что и остальная часть группы.
- Сбой и тайм-аут исполнителя. Настройте выбранный исполнитель на сбой или зависание. Собираются голоса за утверждение тайм-аута, применяется транзакция тайм-аута, и сеанс может продолжиться или завершиться в соответствии с правилами протокола.
Сбой исполнителя и тайм-аут
Настройте выбранного исполнителя на сбой или зависание. Собираются голоса за утверждение тайм-аута, применяется транзакция тайм-аута, и сеанс может продолжиться или завершиться в соответствии с правилами протокола.
- Запрос квитанции Отложите или потеряйте путь ответа исполнителя, а затем запросите у исполнителя запрос на получение. Подтвердите, что квитанция действительна и сеанс пользователя может ее обработать.
Получение вызова
Приостановите или потеряйте путь ответа исполнителя, а затем запросите у исполнителя квитанцию. Подтвердите, что квитанция действительна и сеанс пользователя может ее обработать.
Сценарии восстановления
- Перезапуск хоста SQLite. Выполните несколько выводов, перезапустите один хост-контейнер с сохраненным томом SQLite, продолжите сеанс и завершите его. Подтвердите отсутствие регрессии nonce и перезапущенный хост подписывает окончательное состояние.
Перезапуск хоста SQLite
Выполните несколько выводов, перезапустите один хост-контейнер с сохраненным томом SQLite, продолжите сеанс и завершите его. Подтвердите отсутствие регрессии nonce и перезапущенный хост подписывает окончательное состояние.
- Восстановление Postgres. Запустите счастливый путь с включенным хранилищем Postgres. Перезагрузите все хосты и продолжите сеанс. Утверждение восстановления состояния из Postgres работает, и финализация прошла успешно.
Восстановление Postgres
Пройдите счастливый путь с включенным хранилищем Postgres. Перезагрузите все хосты и продолжите сеанс. Утверждение восстановления состояния из Postgres работает, и финализация прошла успешно.
- Перезапуск всех хостов перед финализацией. Запустите несколько логических выводов, остановите каждый хост, перезапустите их, затем завершите. Утверждайте, что сохраненных различий и подписей достаточно для восстановления.
Перезапуск всего хоста перед финализацией
Запустите несколько выводов, остановите каждый хост, перезапустите их, затем завершите. Утверждайте, что сохраненных различий и подписей достаточно для восстановления.
Версия и сценарии маршрутизации
- Префикс устаревшего маршрута. Запустите сеанс через /v1/devshard/* и убедитесь, что сохраненная версия сеанса — v1.
Префикс устаревшего маршрута
Запустите сеанс через /v1/devshard/* и убедитесь, что сохраненная версия сеанса — v1 .
- Префикс версионного маршрута. Запустите сеанс через /devshard/<version>/* и убедитесь, что сохраненная версия сеанса является выбранной версией.
Префикс версионного маршрута
Запустите сеанс через /devshard/<version>/* и убедитесь, что сохраненная версия сеанса является выбранной версией.
- Конфликт версий. Создайте или восстановите одно и то же условное депонирование в одной версии, а затем попытайтесь прикрепить то же условное депонирование в другой версии. Хранилище утверждений отклоняет конфликт.
Конфликт версий
Создайте или восстановите одно и то же условное депонирование в одной версии, а затем попытайтесь прикрепить то же условное депонирование в другой версии. Хранилище утверждений отклоняет конфликт.
Сценарии метаданных цепочки
- Авторизация по «теплому ключу» Настройте предоставление «теплого ключа» в Mock-chain. Утверждает, что теплый ключ может аутентифицироваться там, где это разрешено, и отклоняется после удаления разрешения или при использовании не для того участника.
Авторизация по теплому ключу
Настройте предоставление теплого ключа в макетной цепочке. Утверждает, что теплый ключ может аутентифицироваться там, где это разрешено, и отклоняется после удаления разрешения или при использовании не для того участника.
- Сбой метаданных моста Внедрить ошибку метаданных моста во время создания или восстановления сеанса. Подтвердить, что хост не готов к сбою или возвращает ожидаемый ответ о недоступности службы.
Ошибка метаданных моста
Внедрить ошибку метаданных моста во время создания или восстановления сеанса. Подтвердить, что хост не готов к сбою или возвращает ожидаемый ответ о недоступности службы.
Утверждения
Тесты E2E должны избегать утверждения только кодов состояния HTTP. Полезные утверждения уровня протокола включают в себя:
ожидаемая форма ответа, совместимая с OpenAI
ожидаемая форма потока SSE
монотонная прогрессия nonce
ожидаемые переходы состояний вывода
сопоставление конечного корня состояния между хостами
ожидаемые подписи по слотам
- Полезная нагрузка расчета включает окончательный номер кода, состояние, версию и подписи.
Выводы метаданных хранилища депонируются до ожидаемой эпохи и версии
перезапущенные хосты восстанавливают последнее известное состояние
неавторизованные подписанты отклоняются
сценарии ошибок создают ожидаемую транзакцию протокола
Расчетный договор
До тех пор, пока пакет E2E не передаст расчет в действующую цепочку, стабильный расчетный контракт должен быть протокольным обязательством, необходимым для проверки на стороне цепочки.
Заявления о базовом урегулировании должны охватывать:
идентификатор условного депонирования
сессионная версия
последний одноразовый номер
окончательный корень состояния или окончательное государственное обязательство
фаза терминального сеанса
конечное состояние для каждого включенного вывода
пороговые подписи
каждая подпись подтверждает окончательное государственное обязательство
каждая подпись сопоставляется с действительным слотом в группе сеансов
дубликаты подписей слотов не учитываются дважды
Экономические поля, такие как учет токенов, комиссии, оставшийся баланс, затраты хоста, пропущенные подсчеты и штрафы за проверку, следует указывать только в специальных сценариях учета. Они не должны быть частью базового контракта по урегулированию дыма до тех пор, пока путь подачи цепочки не станет частью пакета E2E.
Уровни CI
Используйте целенаправленные запуски тестов, а не один большой недифференцированный пакет. CI должен создать необходимые образы Docker с помощью явных целей make перед запуском пакета E2E. Тесты Go должны выбирать уже созданные образы, а не создавать образы для каждого запуска теста.
Примеры целей:
сделать devshard-e2e-images пройти тестирование ./devshard/e2e -run TestE2E_Smoke -count=1 пройти тестирование ./devshard/e2e -run TestE2E_Protocol -count=1 пройти тестирование ./devshard/e2e -run TestE2E_Storage -count=1
devshard-e2e-images должна быть явной целью сборки, которая создает образы, используемые в тестах, включая Mock-chain, devshard-host и devshardctl. Тесты E2E должны быстро провалиться, если эти изображения отсутствуют, вместо того, чтобы автоматически перестраивать их внутри отдельных тестовых случаев.
Рекомендуемые уровни:

@aikuznetsov (https://github.com/aikuznetsov) Пожалуйста, взгляните на это: https://github.com/a-kuprin/gonka/blob/1f0933ad9136cfbcf7070f8210e2c6694731ebaf/devshard/docs/proposals/TESTENV_PROPOSAL.md
Он использует несколько докеров devshardd, 1 devshardctl, 1 dapi-mock и 1 Mock-chain и не использует цепочку.
Он даже уже использовался для тестирования нового протокола синхронизации высоты для devshard: #1209 (https://github.com/gonka-ai/gonka/pull/1209)
Разница в том, что на самом деле используется децентрализованное API (но это имитация протокола). decentrized-api — это MLServer — обслуживающие узлы, а также оракул для параметров и высоты.
Также у меня были некоторые мысли о более высокоуровневых сценариях вместо тестовой среды для создания планов тестирования: https://github.com/a-kuprin/gonka/blob/devshard-testenv/devshard/docs/proposals/PROTOCOL_TESTING_PROPOSAL.md

Goal
Build a real integration test layer for devshard that runs from Go tests but validates the system across Docker containers, real HTTP networking, real process boundaries, and real storage.
This suite should complement the existing unit, package, and httptest tests. Those tests remain the fast correctness layer. The E2E suite verifies that the same protocol works when the pieces are started, wired, restarted, and failed like real services.
Scope
The test runner is Go. The runtime is Docker.
The suite should not depend on a live Cosmos chain, Testermint, or decentralized-api . Chain-facing metadata is served by a local mock service. Inference and validation use deterministic stub engines unless a scenario explicitly opts into a different backend.
Out of scope for the first version:
production observability stack validation
long-running performance or soak testing
live chain settlement submission
real ML model execution
full versiond governance flow
Those can be added later as separate profiles once the core protocol E2E layer is stable.
Test Tools And Frameworks
The first E2E implementation should keep the toolchain small and Go-native.
Recommended tools:
Tools to avoid in the first version:
- Python scenario runners: Go keeps the scenarios close to devshard types, signing helpers, storage helpers, and existing assertions. Adding Python would create a second test runtime before the E2E contract is stable.
- Docker Compose as the primary test orchestrator: testcontainers-go gives each Go test direct control over networks, containers, ports, logs, restarts, and cleanup. Compose can still be useful later for manual reproduction.
- live Cosmos chain or Testermint: the first suite should isolate devshard protocol and transport behavior from chain startup, block production, governance, and unrelated node failures. mock-chain covers the bridge contract needed by devshard.
- real ML model execution: deterministic stub inference keeps tests fast, reproducible, and focused on protocol behavior rather than GPU/model availability or generation quality.
- browser/UI automation: devshard E2E validates HTTP APIs and protocol state. Browser automation would add slow UI concerns that are not part of this proposal.
Test Environment Structure
Each test starts an isolated Docker network. The Go test process stays outside the network and controls the environment through Docker APIs and mapped service ports.
The default smoke environment should spin up:
one mock-chain container
three devshard-host-N containers
one devshardctl container
one postgres container
Storage and fault scenarios add containers or volumes as needed:
persistent SQLite volumes for restart tests
optional per-service control endpoints for deterministic fault injection
flowchart LR TestRunner["Go E2E test runner"] Docker["Docker / testcontainers-go"] Client["HTTP assertions"] subgraph Net["isolated Docker network"] MockChain["mock-chain\nchain metadata + control API"] DevshardCtl["devshardctl\nOpenAI-compatible API"] Host0["devshard-host-0\nslot 0"] Host1["devshard-host-1\nslot 1"] Host2["devshard-host-2\nslot 2"] Postgres["postgres\nsmoke storage backend"] Vol0[("host-0 SQLite volume")] Vol1[("host-1 SQLite volume")] Vol2[("host-2 SQLite volume")] end TestRunner --> Docker TestRunner --> Client Client --> DevshardCtl Client -.direct protocol checks.-> Host0 Client -.direct protocol checks.-> Host1 Client -.direct protocol checks.-> Host2 DevshardCtl --> Host0 DevshardCtl --> Host1 DevshardCtl --> Host2 Host0 <-->|gossip| Host1 Host1 <-->|gossip| Host2 Host2 <-->|gossip| Host0 Host0 --> MockChain Host1 --> MockChain Host2 --> MockChain DevshardCtl --> MockChain Host0 --> Postgres Host1 --> Postgres Host2 --> Postgres Host0 -.sqlite profile.-> Vol0 Host1 -.sqlite profile.-> Vol1 Host2 -.sqlite profile.-> Vol2
Container inventory:
The first implementation should standardize on a three-host group because many protocol behaviors need a majority-like shape: executor rotation, timeout votes, signature accumulation, and gossip convergence. The harness can expose Hosts: N later for stress or edge-case tests.
Runtime Services
Each E2E environment starts an isolated Docker network and a small set of services.
mock-chain
mock-chain is a local metadata service that implements the subset of mainnet bridge behavior needed by devshard.
The first implementation should match the current REST bridge shape exactly. That keeps E2E focused on validating the bridge contract devshard already uses instead of adding a second mock-only API. A cleaner internal control API can be added alongside the REST-compatible endpoints later, but protocol setup and recovery should continue to exercise the same paths as production code.
It serves deterministic local config for:
escrow ID
escrow creator address
escrow balance
epoch ID
app hash
host slot assignments
host inference URLs
token price
validation threshold
warm key grants
approved devshard versions, when a version scenario needs them
It should also expose a dev-only control API for test scenarios:
advance epoch
change approved versions
change host metadata
add or remove warm key grants
inject response delays
inject bridge errors
devshard-host-N
Each host container runs one participant. The process should use the real devshard host, transport, signing, storage, gossip, and state machine code.
Configurable inputs:
escrow ID
host signer key
user address
slot assignment
route prefix
peer host URLs
storage backend
mock-chain URL
stub inference behavior
stub validation behavior
The host should expose the standard devshard transport routes, mounted under either the legacy route prefix or a versioned prefix:
/v1/devshard/* /devshard/<version>/*
devshardctl
The suite should include scenarios that drive requests through the OpenAI-compatible devshardctl surface. This validates the user-facing path:
client -> devshardctl -> devshard transport clients -> host containers
Some lower-level scenarios can talk directly to host transport endpoints when that makes the assertion clearer, but the smoke suite should use devshardctl .
postgres
Postgres is part of the smoke environment and should be the default storage backend for CI smoke tests. SQLite remains useful for local restart tests and single-host persistence edge cases.
Storage scenarios should cover:
SQLite host restart
Postgres host restart
all-host restart
session version conflict
session epoch conflict where applicable
Test Binaries
The E2E suite needs runnable commands that are small wrappers around existing devshard packages.
Recommended commands:
devshard/cmd/devshardd/ main.go devshard/cmd/mock-chain/ main.go
devshardd
devshardd runs one host participant.
For the first E2E implementation, devshardd should be an E2E-only command. It should not be treated as a production binary yet. This keeps the first iteration focused on integration validation, while leaving room to harden and promote the command later if it becomes the right production shape.
It should wire:
bridge client
state machine
host
transport server
storage
gossip peers
inference engine
validation engine
readiness endpoint
dev-only control endpoint when explicitly enabled
For E2E, devshardd can start with stub inference and validation engines. The important point is that the protocol runtime itself is real.
mock-chain
mock-chain serves local metadata and deterministic control behavior. It should start as a simple HTTP server matching the current REST bridge shape. If devshard later moves to a different chain client protocol, the mock should follow that boundary.
Fault Injection
Deterministic fault injection should be part of the test design from the beginning. Without it, timeout and recovery tests become slow and flaky.
The first control surface should support:
fail next inference
delay next inference
hang next inference until cancelled
withhold executor receipt
return a corrupt response hash
return invalid validation result
pause gossip
resume gossip
reject bridge metadata requests
return stale bridge metadata
advance mock epoch
change approved versions
Fault controls must be disabled unless the process is started in explicit test mode.
Scenario Set
Smoke Scenarios
Smoke scenarios should be reliable and fast enough for every CI run.
- Happy path Start three hosts and devshardctl . Send several non-streaming chat completion requests. Finalize the session. Assert the settlement output is present and all hosts agree on the final state.
Happy path
Start three hosts and devshardctl . Send several non-streaming chat completion requests. Finalize the session. Assert the settlement output is present and all hosts agree on the final state.
- Streaming path Send a streaming chat completion request through devshardctl . Assert the client receives content chunks and [DONE] . Assert devshard protocol receipt/meta events are handled internally and do not corrupt the OpenAI-compatible stream.
Streaming path
Send a streaming chat completion request through devshardctl . Assert the client receives content chunks and [DONE] . Assert devshard protocol receipt/meta events are handled internally and do not corrupt the OpenAI-compatible stream.
- Auth rejection Send a protected host request signed by an unauthorized key. Assert the request is rejected with an authorization error.
Auth rejection
Send a protected host request signed by an unauthorized key. Assert the request is rejected with an authorization error.
Protocol Scenarios
- Gossip convergence Submit work while all hosts are running. Assert nonce, mempool, and signature data propagate between participants and converge.
Gossip convergence
Submit work while all hosts are running. Assert nonce, mempool, and signature data propagate between participants and converge.
- Host catch-up Let one host miss earlier diffs, then send it a later request with catch-up diffs. Assert it reaches the same state root as the rest of the group.
Host catch-up
Let one host miss earlier diffs, then send it a later request with catch-up diffs. Assert it reaches the same state root as the rest of the group.
- Executor failure and timeout Configure the selected executor to fail or hang. Assert timeout votes are collected, the timeout transaction is applied, and the session can continue or finalize according to protocol rules.
Executor failure and timeout
Configure the selected executor to fail or hang. Assert timeout votes are collected, the timeout transaction is applied, and the session can continue or finalize according to protocol rules.
- Receipt challenge Withhold or lose the executor response path, then challenge the executor for a receipt. Assert the receipt is valid and the user session can process it.
Receipt challenge
Withhold or lose the executor response path, then challenge the executor for a receipt. Assert the receipt is valid and the user session can process it.
Recovery Scenarios
- SQLite host restart Run several inferences, restart one host container with its SQLite volume preserved, continue the session, and finalize. Assert there is no nonce regression and the restarted host signs the final state.
SQLite host restart
Run several inferences, restart one host container with its SQLite volume preserved, continue the session, and finalize. Assert there is no nonce regression and the restarted host signs the final state.
- Postgres recovery Run the happy path with Postgres storage enabled. Restart all hosts and continue the session. Assert state recovery from Postgres works and finalization succeeds.
Postgres recovery
Run the happy path with Postgres storage enabled. Restart all hosts and continue the session. Assert state recovery from Postgres works and finalization succeeds.
- All-host restart before finalization Run several inferences, stop every host, restart them, then finalize. Assert persisted diffs and signatures are sufficient to recover.
All-host restart before finalization
Run several inferences, stop every host, restart them, then finalize. Assert persisted diffs and signatures are sufficient to recover.
Version And Routing Scenarios
- Legacy route prefix Run a session through /v1/devshard/* and assert the stored session version is v1 .
Legacy route prefix
Run a session through /v1/devshard/* and assert the stored session version is v1 .
- Versioned route prefix Run a session through /devshard/<version>/* and assert the stored session version is the selected version.
Versioned route prefix
Run a session through /devshard/<version>/* and assert the stored session version is the selected version.
- Version conflict Create or recover the same escrow under one version, then attempt to attach the same escrow under a different version. Assert storage rejects the conflict.
Version conflict
Create or recover the same escrow under one version, then attempt to attach the same escrow under a different version. Assert storage rejects the conflict.
Chain Metadata Scenarios
- Warm key authorization Configure a warm key grant in mock-chain . Assert the warm key can authenticate where allowed and is rejected after the grant is removed or when used for the wrong participant.
Warm key authorization
Configure a warm key grant in mock-chain . Assert the warm key can authenticate where allowed and is rejected after the grant is removed or when used for the wrong participant.
- Bridge metadata failure Inject a bridge metadata error during session creation or recovery. Assert the host fails ready or returns the expected service-unavailable response.
Bridge metadata failure
Inject a bridge metadata error during session creation or recovery. Assert the host fails ready or returns the expected service-unavailable response.
Assertions
E2E tests should avoid asserting only HTTP status codes. Useful protocol-level assertions include:
expected OpenAI-compatible response shape
expected SSE stream shape
monotonic nonce progression
expected inference status transitions
matching final state root across hosts
expected signatures by slot
settlement payload includes final nonce, state, version, and signatures
storage metadata pins escrow to the expected epoch and version
restarted hosts recover latest known state
unauthorized signers are rejected
fault scenarios produce the expected protocol transaction
Settlement Contract
Until the E2E suite submits settlement to a live chain, the stable settlement contract should be the protocol commitment needed for chain-side verification.
Baseline settlement assertions should cover:
escrow ID
session version
final nonce
final state root or final state commitment
terminal session phase
terminal state for every included inference
threshold-sufficient signatures
each signature verifies over the final state commitment
each signature maps to a valid slot in the session group
duplicate slot signatures are not counted twice
Economic fields such as token accounting, fees, remaining balance, host costs, missed counts, and validation penalties should be asserted only in dedicated accounting scenarios. They should not be part of the baseline smoke settlement contract until the chain submission path is part of the E2E suite.
CI Tiers
Use focused go test runs rather than one large undifferentiated suite. CI should build the required Docker images through explicit make targets before running the E2E suite. The Go tests should select already-built images rather than building images per test run.
Example targets:
make devshard-e2e-images go test ./devshard/e2e -run TestE2E_Smoke -count=1 go test ./devshard/e2e -run TestE2E_Protocol -count=1 go test ./devshard/e2e -run TestE2E_Storage -count=1
devshard-e2e-images should be an explicit build target that produces the images used by the tests, including mock-chain , devshard-host , and devshardctl . The E2E tests should fail fast if those images are missing instead of silently rebuilding them inside individual test cases.
Recommended tiers:

@aikuznetsov (https://github.com/aikuznetsov) Please take a look on this: https://github.com/a-kuprin/gonka/blob/1f0933ad9136cfbcf7070f8210e2c6694731ebaf/devshard/docs/proposals/TESTENV_PROPOSAL.md
It is using multiple devshardd, 1 devshardctl, 1 dapi-mock and 1 mock-chain dockers and doesn't use chain.
It even already used for testing new height-sync protocol for devshard: #1209 (https://github.com/gonka-ai/gonka/pull/1209)
The difference is that actually decentralized-api is used (but mock for protocol). decentralized-api is the MLServer - serving nodes, and also oracle for parameters and height.
Also I had some thoughts on more high-level scripting over test-environment for creating test plans: https://github.com/a-kuprin/gonka/blob/devshard-testenv/devshard/docs/proposals/PROTOCOL_TESTING_PROPOSAL.md
Цель
Создайте настоящий уровень интеграционного тестирования для devshard, который запускается на основе тестов Go, но проверяет систему с помощью контейнеров Docker, реальной сети HTTP, реальных границ процессов и реального хранилища.
Этот пакет должен дополнять существующие модульные, пакетные и httptest-тесты. Эти тесты остаются слоем быстрой корректности. Пакет E2E проверяет, что один и тот же протокол работает при запуске, подключении, перезапуске и сбое компонентов, как и реальные службы.
Область применения
Тест-раннер — Go. Среда выполнения — Docker.
Пакет не должен зависеть от действующей цепочки Cosmos, Testermint или decentralized-api. Метаданные, связанные с цепочкой, обслуживаются локальным фиктивным сервисом. Для вывода и проверки используются детерминированные механизмы-заглушки, если в сценарии явно не выбран другой бэкэнд.
За пределами первой версии:
проверка стека наблюдения за добычей
долговременная производительность или тестирование на выдержку
подача расчетов в живой цепочке
реальное выполнение модели машинного обучения
полный поток управления версиями
Их можно будет добавить позже как отдельные профили, как только уровень основного протокола E2E станет стабильным.
Инструменты и платформы тестирования
Первая реализация E2E должна сохранить небольшой набор инструментов и использовать Go-native.
Рекомендуемые инструменты:
Инструменты, которых следует избегать в первой версии:
Структура тестовой среды
Каждый тест запускает изолированную сеть Docker. Процесс тестирования Go остается вне сети и контролирует среду через API-интерфейсы Docker и сопоставленные сервисные порты.
Среда дыма по умолчанию должна развернуться:
один контейнер макетной цепочки
три контейнера devshard-host-N
один контейнер devshardctl
один контейнер Postgres
Сценарии хранения и сбоя добавляют контейнеры или тома по мере необходимости:
постоянные тома SQLite для тестов перезапуска
дополнительные конечные точки управления для каждой службы для детерминированного внедрения ошибок
блок-схема LR TestRunner["Go E2E test runner"] Docker["Docker / testcontainers-go"] Client["HTTP утверждения"] subgraph Net["изолированная сеть Docker"] MockChain["mock-chain\nchain метаданные + API управления"] DevshardCtl["devshardctl\nOpenAI-совместимый API"] Host0["devshard-host-0\nslot 0"] Host1["devshard-host-1\nslot 1"] Host2["devshard-host-2\nslot 2"] Postgres["postgres\nsmoke Storage Backend"] Vol0[("host-0 SQLite Volume")] Vol1[("host-1 SQLite Volume")] Vol2[("host-2 SQLite Volume")] end TestRunner --> Docker TestRunner --> Client Client --> Клиент DevshardCtl -.прямые проверки протокола.-> Клиент Host0 -.прямые проверки протокола.-> Клиент Host1 -.прямые проверки протокола.-> Host2 DevshardCtl --> Host0 DevshardCtl --> Host1 DevshardCtl --> Host2 Host0 <-->|gossip| Хост1 Хост1 <-->|сплетни| Хост2 Хост2 <-->|сплетни| Host0 Host0 --> MockChain Host1 --> MockChain Host2 --> MockChain DevshardCtl --> MockChain Host0 --> Postgres Host1 --> Postgres Host2 --> Postgres Host0 -.sqlite профиль.-> Vol0 Host1 -.sqlite профиль.-> Vol1 Host2 -.sqlite профиль.-> Vol2Инвентаризация контейнеров:
Первая реализация должна быть стандартизирована для группы из трех хостов, поскольку многие варианты поведения протокола требуют формы, подобной большинству: ротация исполнителей, голосование по тайм-ауту, накопление подписей и сходимость слухов. Жгут может позже подвергать Hosts: N стресс-тестам или тестам в крайних случаях.
Службы времени выполнения
Каждая среда E2E запускает изолированную сеть Docker и небольшой набор сервисов.
макет цепи
Mock-chain — это локальная служба метаданных, которая реализует подмножество поведения моста основной сети, необходимое для devshard.
Первая реализация должна точно соответствовать текущей форме моста REST. Это заставляет E2E сосредоточиться на проверке мостового контракта, который разработчик уже использует, вместо добавления второго API, предназначенного только для макетов. Более чистый API внутреннего контроля можно добавить позже вместе с REST-совместимыми конечными точками, но настройка и восстановление протокола должны продолжать выполняться по тем же путям, что и рабочий код.
Он служит детерминированной локальной конфигурацией для:
идентификатор условного депонирования
адрес создателя условного депонирования
баланс условного депонирования
идентификатор эпохи
хэш приложения
назначение слотов хоста
URL-адреса вывода хоста
цена токена
порог проверки
гранты «теплый ключ»
утвержденные версии devshard, когда они нужны сценарию версии
Он также должен предоставить API управления только для разработчиков для тестовых сценариев:
передовая эпоха
изменить утвержденные версии
изменить метаданные хоста
добавить или удалить гранты «теплых ключей»
ввести задержки ответа
вводить ошибки моста
devshard-host-N
В каждом хост-контейнере работает один участник. Процесс должен использовать реальный хост devshard, транспорт, подпись, хранилище, сплетни и код конечного автомата.
Конфигурируемые входы:
идентификатор условного депонирования
ключ подписывающего лица хоста
адрес пользователя
назначение слота
префикс маршрута
URL-адреса одноранговых хостов
серверная часть хранилища
URL-адрес макетной цепочки
поведение заглушки вывода
поведение проверки заглушки
Хост должен предоставлять стандартные транспортные маршруты devshard, смонтированные либо с префиксом устаревшего маршрута, либо с префиксом версии:
/v1/devshard/* /devshard/<версия>/*
devshardctl
Пакет должен включать сценарии, которые направляют запросы через OpenAI-совместимую поверхность devshardctl. Это подтверждает путь, обращенный к пользователю:
клиент -> devshardctl -> транспортные клиенты devshard -> хост-контейнеры
Некоторые сценарии более низкого уровня могут напрямую обращаться к конечным точкам транспорта хоста, когда это делает утверждение более ясным, но пакет дыма должен использовать devshardctl .
постгрес
Postgres является частью дымовой среды и должен быть базовым хранилищем по умолчанию для дымовых тестов CI. SQLite остается полезным для локальных тестов перезапуска и крайних случаев сохранения состояния одного хоста.
Сценарии хранения должны охватывать:
Перезапуск хоста SQLite
Перезапуск хоста Postgres
перезапуск всех хостов
конфликт версий сеанса
конфликт эпохи сеанса, где это применимо
Тестовые двоичные файлы
Пакету E2E нужны исполняемые команды, которые представляют собой небольшие оболочки существующих пакетов devshard.
Рекомендуемые команды:
devshard/cmd/devshardd/main.go devshard/cmd/mock-chain/main.go
девшардд
devshardd запускает одного хост-участника.
Для первой реализации E2E devshardd должна быть командой только для E2E. Его пока не следует рассматривать как производственный двоичный файл. Это позволяет сосредоточить первую итерацию на проверке интеграции, оставляя при этом место для усиления и продвижения команды позже, если она примет правильную производственную форму.
Он должен подключить:
мостовой клиент
государственная машина
хозяин
транспортный сервер
хранение
коллеги по сплетням
машина вывода
механизм проверки
конечная точка готовности
конечная точка управления только для разработчиков, если она явно включена
Для E2E devshardd может начать с механизмов вывода и проверки заглушки. Важным моментом является то, что сама среда выполнения протокола реальна.
макет цепи
Mock-chain обслуживает локальные метаданные и детерминированное поведение управления. Он должен начинаться как простой HTTP-сервер, соответствующий текущей форме моста REST. Если позже devshard перейдет на другой протокол клиента цепочки, макет должен следовать этой границе.
Внесение ошибок
Детерминированное внесение ошибок должно быть частью проекта тестирования с самого начала. Без него тесты тайм-аута и восстановления становятся медленными и нестабильными.
Первая поверхность управления должна поддерживать:
провалить следующий вывод
отложить следующий вывод
отложить следующий вывод до его отмены
удержать расписку исполнителя
вернуть поврежденный хэш ответа
вернуть неверный результат проверки
приостановить сплетни
возобновить сплетни
отклонять запросы метаданных моста
вернуть устаревшие метаданные моста
предварительная имитация эпохи
изменить утвержденные версии
Средства контроля неисправностей должны быть отключены, если процесс не запущен в явном тестовом режиме.
Набор сценариев
Сценарии дыма
Сценарии дыма должны быть надежными и достаточно быстрыми для каждого запуска CI.
Счастливый путь
Запустите три хоста и devshardctl. Отправьте несколько запросов на завершение непотокового чата. Завершите сеанс. Подтвердите, что выходные данные расчета присутствуют и все хосты согласны с окончательным состоянием.
Путь потоковой передачи
Отправьте запрос на завершение потокового чата через devshardctl. Подтвердите, что клиент получил фрагменты контента и [DONE] . Получение/мета-события протокола Assert devshard обрабатываются внутри и не повреждают поток, совместимый с OpenAI.
Отказ в аутентификации
Отправьте запрос защищенного хоста, подписанный неавторизованным ключом. Укажите, что запрос отклонен из-за ошибки авторизации.
Сценарии протоколов
Сближение сплетен
Отправляйте работу, пока все хосты работают. Данные подтверждения nonce, мемпула и подписи распространяются между участниками и сходятся.
Догонялка хоста
Позвольте одному хосту пропустить более ранние различия, а затем отправьте ему более поздний запрос с догоняющими различиями. Утвердите, что он достигает того же корня состояния, что и остальная часть группы.
Сбой исполнителя и тайм-аут
Настройте выбранного исполнителя на сбой или зависание. Собираются голоса за утверждение тайм-аута, применяется транзакция тайм-аута, и сеанс может продолжиться или завершиться в соответствии с правилами протокола.
Получение вызова
Приостановите или потеряйте путь ответа исполнителя, а затем запросите у исполнителя квитанцию. Подтвердите, что квитанция действительна и сеанс пользователя может ее обработать.
Сценарии восстановления
Перезапуск хоста SQLite
Выполните несколько выводов, перезапустите один хост-контейнер с сохраненным томом SQLite, продолжите сеанс и завершите его. Подтвердите отсутствие регрессии nonce и перезапущенный хост подписывает окончательное состояние.
Восстановление Postgres
Пройдите счастливый путь с включенным хранилищем Postgres. Перезагрузите все хосты и продолжите сеанс. Утверждение восстановления состояния из Postgres работает, и финализация прошла успешно.
Перезапуск всего хоста перед финализацией
Запустите несколько выводов, остановите каждый хост, перезапустите их, затем завершите. Утверждайте, что сохраненных различий и подписей достаточно для восстановления.
Версия и сценарии маршрутизации
Префикс устаревшего маршрута
Запустите сеанс через /v1/devshard/* и убедитесь, что сохраненная версия сеанса — v1 .
Префикс версионного маршрута
Запустите сеанс через /devshard/<version>/* и убедитесь, что сохраненная версия сеанса является выбранной версией.
Конфликт версий
Создайте или восстановите одно и то же условное депонирование в одной версии, а затем попытайтесь прикрепить то же условное депонирование в другой версии. Хранилище утверждений отклоняет конфликт.
Сценарии метаданных цепочки
Авторизация по теплому ключу
Настройте предоставление теплого ключа в макетной цепочке. Утверждает, что теплый ключ может аутентифицироваться там, где это разрешено, и отклоняется после удаления разрешения или при использовании не для того участника.
Ошибка метаданных моста
Внедрить ошибку метаданных моста во время создания или восстановления сеанса. Подтвердить, что хост не готов к сбою или возвращает ожидаемый ответ о недоступности службы.
Утверждения
Тесты E2E должны избегать утверждения только кодов состояния HTTP. Полезные утверждения уровня протокола включают в себя:
ожидаемая форма ответа, совместимая с OpenAI
ожидаемая форма потока SSE
монотонная прогрессия nonce
ожидаемые переходы состояний вывода
сопоставление конечного корня состояния между хостами
ожидаемые подписи по слотам
Выводы метаданных хранилища депонируются до ожидаемой эпохи и версии
перезапущенные хосты восстанавливают последнее известное состояние
неавторизованные подписанты отклоняются
сценарии ошибок создают ожидаемую транзакцию протокола
Расчетный договор
До тех пор, пока пакет E2E не передаст расчет в действующую цепочку, стабильный расчетный контракт должен быть протокольным обязательством, необходимым для проверки на стороне цепочки.
Заявления о базовом урегулировании должны охватывать:
идентификатор условного депонирования
сессионная версия
последний одноразовый номер
окончательный корень состояния или окончательное государственное обязательство
фаза терминального сеанса
конечное состояние для каждого включенного вывода
пороговые подписи
каждая подпись подтверждает окончательное государственное обязательство
каждая подпись сопоставляется с действительным слотом в группе сеансов
дубликаты подписей слотов не учитываются дважды
Экономические поля, такие как учет токенов, комиссии, оставшийся баланс, затраты хоста, пропущенные подсчеты и штрафы за проверку, следует указывать только в специальных сценариях учета. Они не должны быть частью базового контракта по урегулированию дыма до тех пор, пока путь подачи цепочки не станет частью пакета E2E.
Уровни CI
Используйте целенаправленные запуски тестов, а не один большой недифференцированный пакет. CI должен создать необходимые образы Docker с помощью явных целей make перед запуском пакета E2E. Тесты Go должны выбирать уже созданные образы, а не создавать образы для каждого запуска теста.
Примеры целей:
сделать devshard-e2e-images пройти тестирование ./devshard/e2e -run TestE2E_Smoke -count=1 пройти тестирование ./devshard/e2e -run TestE2E_Protocol -count=1 пройти тестирование ./devshard/e2e -run TestE2E_Storage -count=1devshard-e2e-images должна быть явной целью сборки, которая создает образы, используемые в тестах, включая Mock-chain, devshard-host и devshardctl. Тесты E2E должны быстро провалиться, если эти изображения отсутствуют, вместо того, чтобы автоматически перестраивать их внутри отдельных тестовых случаев.
Рекомендуемые уровни: