113 lines
12 KiB
Markdown
113 lines
12 KiB
Markdown
# Task 04: CI и проверяемость тестов (hermes-android)
|
||
|
||
**Repo:** `ochenstarik-ui/hermes-android`
|
||
**Assigned to:** Antigravity (режим оркестратора, два кодера)
|
||
**Priority:** HIGH (без этого ни одно предыдущее задание не защищено от регресса)
|
||
**Date:** 2026-08-24
|
||
**Base SHA:** результат задания 03 — указать фактический SHA при выдаче
|
||
**Зависимость:** задания 01–03 приняты. Их тесты — материал, на котором проверяется работоспособность CI.
|
||
|
||
## Роли и протокол
|
||
|
||
| Роль | Модель | Что делает |
|
||
|---|---|---|
|
||
| Оркестратор | Antigravity | Разбивает работу, маршрутизирует, принимает результат |
|
||
| Кодер 1 | Gemini Flash 3.7 high | Реализация §Scope целиком |
|
||
| Кодер 2 | Gemini Pro high | Независимая проверка работы кодера 1 и доработка |
|
||
|
||
**Раунд 1 (кодер 1).** Пункты §Scope по порядку. Для этого задания тесты-первыми неприменимы к пунктам 1–2 (инфраструктура) — вместо этого требуется демонстрация: намеренно сломанный коммит в черновой ветке валит пайплайн, вывод приложен к отчёту. Нерешённое — в `OPEN QUESTIONS`.
|
||
**Раунд 2 (кодер 2).** Независимо запускает пайплайн своим прогоном, проходит §Anti-checklist с явной отметкой каждого пункта, доводит минимальным дифом, даёт findings по шкале.
|
||
**Раунд 3 (оркестратор).** Приёмка только при ссылке на фактический прогон CI (job URL или лог), а не на конфиг.
|
||
|
||
## Проблема
|
||
|
||
**1. CI отсутствует полностью (`BUILD-03`).** Каталога `.github` в репозитории нет, хотя README документирует `gradlew test` и `gradlew lint`. 75 юнит-тестов и тесты `hermes-pair` никем не запускаются при пуше — все коммиты с пометками «fix P0/P1» проверялись только локально и на слово.
|
||
|
||
**2. Объявлен runner несуществующих тестов (`BUILD-05`).** `app/build.gradle.kts:19` задаёт `testInstrumentationRunner`, но исходного набора `androidTest` нет, а из зависимостей подключён только `androidTestImplementation(composeBom)` — без `androidx.test.ext:junit`, `espresso`, `compose-ui-test-junit4`. Инструментальные тесты, требуемые заданиями 02 и 03, запускать нечем.
|
||
|
||
**3. Фейки скрывают дефекты (`TEST-01`).** `FakeDaos.kt:70-76` возвращает сообщения в порядке вставки — маскирует `DATA-02`. Внутри фейка обычные `mutableMap` без синхронизации, при этом часть тестов гоняется на `runBlocking(Dispatchers.Default)`: сами тесты могут падать с `ConcurrentModificationException`.
|
||
|
||
**4. Нет инструментальных и UI-тестов (`TEST-02`).** 75 тестов — все JVM-only. Не покрыты миграции Room, реальное поведение `EncryptedSharedPreferences`, экран чата, сканер, навигация, поворот экрана — то есть ровно то, что чинили задания 02 и 03.
|
||
|
||
**5. Ожидания через `delay(50)` (`TEST-03`).** `JsonRpcGatewayClientTest.kt:100`, `EndToEndContractScenarioTest.kt:173,183,199`, `ApprovalRoutingTest.kt:209` — пять мест синхронизируются реальными задержками. На CI под нагрузкой такие тесты мигают, и первым делом их начнут отключать.
|
||
|
||
**6. Тест покрывает несуществующий код (`TEST-04`).** `EndToEndContractScenarioTest.kt:45` проверяет `HermesGatewayRepository` — класс прошлой архитектуры, недостижимый из `MainActivity`. Тест зелёный и ничего не гарантирует.
|
||
|
||
## Scope
|
||
|
||
**1. Пайплайн (`BUILD-03`).**
|
||
`.github/workflows/ci.yml`: на `push` и `pull_request`.
|
||
Джоб `android`: JDK 17, Android SDK, кэш Gradle, шаги `testDebugUnitTest`, `lint`, `assembleDebug`. Отчёты тестов и lint — артефактами.
|
||
Джоб `rust`: `cargo test --all-targets` и `cargo clippy -- -D warnings` в `hermes-pair/`, кэш cargo.
|
||
Джоб `instrumented`: `connectedDebugAndroidTest` на эмуляторе (`reactivecircus/android-emulator-runner` или эквивалент). Если эмулятор на раннере недоступен — джоб объявляется обязательным, но помечается известным ограничением в отчёте; молча выключать его нельзя.
|
||
Настроить branch protection на `main`: merge блокируется при падении обязательных джобов. Если прав на настройку у исполнителя нет — зафиксировать в отчёте как действие для владельца, а не пропустить.
|
||
|
||
**2. Инфраструктура инструментальных тестов (`BUILD-05`).**
|
||
Добавить `androidx.test.ext:junit`, `espresso-core`, `androidx.compose.ui:ui-test-junit4`, `androidx.room:room-testing`. Создать `app/src/androidTest/…` и убедиться, что тесты заданий 02 и 03 действительно запускаются.
|
||
|
||
**3. Ревизия фейков (`TEST-01`).**
|
||
`FakeUnifiedSessionDao` и `FakeHostDao` — на потокобезопасные структуры. Порядок возврата сообщений — перемешанный (см. задание 02 §Scope 2), чтобы фейк не был мягче реального Room.
|
||
Пройти по всем существующим тестам и отметить те, что зелены только благодаря удобному фейку. Список — в отчёт, с пометкой, какие из них требуют переписывания (сами переписывания — по пункту 4, если укладываются, иначе отдельным заданием).
|
||
|
||
**4. Детерминированные ожидания (`TEST-03`).**
|
||
Пять мест с `delay(50)` перевести на `runTest` с виртуальным временем и `Turbine` (зависимость `app.cash.turbine:turbine:1.2.0` уже подключена). Семантику проверок не менять — только способ ожидания.
|
||
|
||
**5. Тест на актуальный путь (`TEST-04`).**
|
||
`EndToEndContractScenarioTest` переписать на `UnifiedSessionRepository`, сохранив покрываемые сценарии (полный жизненный цикл контракта, обновление токена). Старую версию удалить только после того, как новая проходит и покрывает те же сценарии — соответствие перечислить построчно в отчёте.
|
||
Сам класс `HermesGatewayRepository` **не удалять** — это задание 10.
|
||
|
||
## Do not change
|
||
|
||
- Продуктовый код заданий 01–03: CI не повод «попутно поправить».
|
||
- Версии AGP, Kotlin, Compose BOM — задание 05.
|
||
- Логику существующих тестов: меняется способ ожидания, не ассерты.
|
||
- `hermes-pair/**` исходники — здесь только запуск его тестов.
|
||
|
||
## Anti-checklist
|
||
|
||
1. Workflow добавлен, но ни разу не запускался — «должно работать». Требование: приложить ссылку на фактический прогон и на прогон с намеренно сломанным тестом.
|
||
2. Джоб `instrumented` добавлен с `continue-on-error: true` — падение не блокирует merge, то есть джоба нет. Проверить.
|
||
3. `cargo clippy` без `-D warnings` — предупреждения не валят сборку, смысл потерян.
|
||
4. Кэш Gradle настроен так, что валидный прогон переиспользует результаты предыдущего и тесты фактически не выполняются. Проверить по времени и логу, что тесты реально шли.
|
||
5. Фейки объявлены потокобезопасными заменой `mutableMapOf` на `ConcurrentHashMap`, но итерация по ним осталась небезопасной в другом методе. Проверить все методы фейка.
|
||
6. Порядок в фейке перемешан, но тест задания 02 после этого стал падать — и его «поправили» вместо кода. Это ослабление, запрещено.
|
||
7. `delay(50)` заменён на `advanceUntilIdle()`, но ассерт при этом ослаблен или перенесён. Сверить ассерты до и после построчно.
|
||
8. Новый `EndToEndContractScenarioTest` покрывает меньше сценариев, чем старый, а старый уже удалён. Требование: удаление только после построчного соответствия.
|
||
9. В отчёте `green` для незапущенной команды (`AGENTS.md §3`).
|
||
|
||
## Definition of Done
|
||
|
||
- Пуш с намеренно сломанным юнит-тестом валит пайплайн — приложен лог.
|
||
- Пуш с намеренно сломанным `cargo clippy` валит пайплайн — приложен лог.
|
||
- Инструментальные тесты заданий 02 и 03 запускаются в CI и проходят.
|
||
- Ни один тест не запускается с `delay`-ожиданием; все пять мест переведены.
|
||
- Ни один тест не удалён без построчно обоснованной замены.
|
||
- Фейки потокобезопасны, порядок сообщений в них не мягче реального Room.
|
||
- `main` защищён от merge при падении обязательных джобов, либо это зафиксировано как требующее действия владельца.
|
||
|
||
## Required tests
|
||
|
||
Отдельных продуктовых тестов задание не добавляет. Доказательства — прогоны:
|
||
|
||
1. прогон на текущем `HEAD`: всё зелёное;
|
||
2. прогон с намеренно сломанным юнит-тестом: красный, указано какой шаг;
|
||
3. прогон с намеренно сломанным clippy-предупреждением: красный;
|
||
4. прогон инструментальных тестов: список выполненных, не «0 tests».
|
||
|
||
Все четыре — с ссылками или полными логами, обоими кодерами независимо.
|
||
|
||
## Required verification
|
||
|
||
```text
|
||
./gradlew --no-daemon testDebugUnitTest
|
||
./gradlew --no-daemon connectedDebugAndroidTest
|
||
./gradlew --no-daemon lint
|
||
./gradlew --no-daemon assembleDebug
|
||
cd hermes-pair && cargo test --all-targets && cargo clippy -- -D warnings
|
||
```
|
||
|
||
Локально и в CI. Расхождение локального и CI-результата — само по себе находка, фиксируется в отчёте.
|
||
|
||
## Result
|
||
|
||
`agents/antigravity/done/TASK-2026-08-24-04-ci-and-test-harness.md` — разделы `## Кодер 1`, `## Кодер 2 (review + доработка)`, `## Вердикт оркестратора`. Содержимое по `AGENTS.md §4`.
|