Путь проверен 05.10.2026 на чистом worktree: compose, migrate/seed, бэкенд, фронт через rsbuild dev с REACT_APP_API_URL/REACT_APP_ROOT_PATH, dev-вход, /admin (headless Chromium), API и импорт (curl). Closes #11 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
23 KiB
Гайд тестировщика: локальный стенд АРМ КА (Срезы 0–1)
Документ для ручного тестирования текущего main. Путь проверен 05.10.2026 на macOS (Docker 28, compose 2.34,
Node 24, uv 0.10): от пустого каталога до экрана администратора — около 15 минут, из них 10 — скачивание образов.
1. Что сейчас есть и чего нет
Есть (Срез 0 + Срез 1):
- Бэкенд
arm-core(FastAPI,/api/v1): вход по email в dev-режиме, профиль пользователя, права как данные. - API справочников: группы компаний (ГК), юрлица и ИП (ЮЛ) с историей, переводом между ГК и закрытием, алиасы контрагентов, группы связанных компаний и заёмщиков (ГСК/ГСЗ) с составом, филиалы. Видимость по филиалу: головной офис видит всё, филиал — только свои ГК.
- Импорт справочников из выгрузки ЕЦП CRM (xlsx/csv) с пробным прогоном и отчётом.
- Администрирование: пользователи, роли, матрица «роль × право», защита последнего администратора.
- Фронтенд: страница
/admin(«Пользователи и роли»). Другого UI нет. - Журнал аудита в БД (
dkb.audit_event).
Нет (это не баги):
- Экранов справочников ГК/ЮЛ/ГСК — проверяются только через Swagger (
/docs) или curl. - Загрузки файлов отчётности, парсера ОСВ, распознавания PDF, консолидации, КФО — следующие срезы.
- Настоящего входа через SSO банка. Вход — dev-форма по email без пароля.
2. Что установить и проверить из ПО
| Что | Версия | Проверка | Если нет |
|---|---|---|---|
| ОС | macOS или Linux. Windows — только через WSL2 (Ubuntu); нативно не проверялось | — | — |
| Git + SSH-ключ в Gitea | любая свежая | ssh -T -p 2222 git@git.aepif.ru отвечает приветствием |
добавить публичный ключ в профиль Gitea git.aepif.ru |
| Docker + compose v2 | Docker ≥ 24, compose ≥ 2.20 (проверено 28.0 / 2.34) | docker compose version |
Docker Desktop (macOS) или docker-ce + docker-compose-plugin (Linux) |
uv |
≥ 0.4 (проверено 0.10) | uv --version |
curl -LsSf https://astral.sh/uv/install.sh | sh; Python 3.11 uv скачает сам |
| Node.js + npm | Node ≥ 20 LTS (проверено 24.7) | node --version && npm --version |
nodejs.org или nvm install --lts |
make, curl |
любые | make --version, curl --version |
macOS: Xcode Command Line Tools (xcode-select --install); Linux: build-essential |
| Свободные порты | 5432, 4222, 8222, 9000, 9001 (compose), 8000 (бэкенд), 3000 (фронт) | lsof -i :5432 и т. д. пусто |
закрыть локальный PostgreSQL/MinIO или сменить порты в docs/dev/.env и arm-core/api/.env |
| Ресурсы | Docker: ≥ 4 ГБ RAM, ≥ 3 ГБ диска под образы и тома | — | — |
| Доступ в интернет | нужен один раз: образы Docker Hub, PyPI, npm | — | Nexus банка не требуется |
Необязательно, но удобно: клиент PostgreSQL (DBeaver или psql) для просмотра аудита; Postman или аналог — Swagger
на /docs достаточно.
Браузер — любой современный (Chrome, Firefox, Safari, Edge).
3. Клонирование
Три репозитория должны лежать рядом, в одном каталоге, под именами docs, arm-core, fixtures (в коде пути
вида ../docs/...):
mkdir arm-ka && cd arm-ka
git clone ssh://git@git.aepif.ru:2222/RSHB/arm-ka-docs.git docs
git clone ssh://git@git.aepif.ru:2222/RSHB/arm-core.git arm-core
git clone ssh://git@git.aepif.ru:2222/RSHB/arm-ka-fixtures.git fixtures
fixtures содержит реальные данные (внутренний доступ) — нужен для файла импорта. Если доступа нет, пункт 6.3
чек-листа можно выполнить на полной выгрузке из docs/source/biq/.
4. Запуск стенда
Все команды — из каталога arm-core, если не сказано иное.
4.1 Инфраструктура (PostgreSQL, NATS, MinIO)
cp ../docs/dev/.env.example ../docs/dev/.env
make up
docker compose -f ../docs/dev/docker-compose.yml up -d --wait postgres nats minio
docker compose -f ../docs/dev/docker-compose.yml ps
Ожидание: postgres, nats, minio — Up (healthy); minio-init — Exited (0) (это нормально: он создаёт бакет и
завершается). Первый запуск скачивает образы, это самая долгая часть.
4.2 Бэкенд
make venv # api/.venv на Python 3.11, зависимости из PyPI
cp api/.env.example api/.env # dev-настройки: DEBUG=true, AUTH_DEV_ISSUER=true
make migrate # схема БД
make seed-dev # справочники + 7 dev-пользователей
cd api && .venv/bin/python run_server.py
Ожидание: в логе нет трейсбэков; curl -s localhost:8000/healthcheck отвечает {"uptime": ..., "nats": "ok"}.
Swagger: http://localhost:8000/docs. Сервер слушает только 127.0.0.1, так и оставьте.
4.3 Фронтенд (второй терминал)
cd arm-core/web
npm install
REACT_APP_API_URL=http://localhost:8000 REACT_APP_ROOT_PATH=/ npm run start
Откроется http://localhost:3000/ с формой «Вход для разработки». Две переменные окружения обязательны: без них фронт ищет API на своём порту и путь приложения как на платформе банка.
Не используйте npm run build для тестирования: в production-сборке dev-формы входа нет, страница показывает
«Вход выполняется через РАИСА».
4.4 Вход
В форме введите email dev-пользователя и нажмите «Войти (dev)». Пароля нет. Токен живёт в текущей вкладке
(sessionStorage), новая вкладка — новый вход. Токен действует 8 часов.
| Роль | Филиал | Что видит | |
|---|---|---|---|
admin@armka.local |
ADMIN | Головной офис | всё; единственный с правом администрирования и импорта |
ka_go@armka.local |
KA_GO | Головной офис | все ГК и ЮЛ, может создавать и менять |
curator_go@armka.local |
CURATOR_GO | Головной офис | все ГК и ЮЛ, может создавать и менять |
expert@armka.local |
EXPERT | Головной офис | всё, только чтение |
ka_rf@armka.local |
KA_RF | Ростовский РФ | только ГК своего филиала, может создавать и менять |
rka@armka.local |
RKA | Ростовский РФ | только ГК своего филиала |
client_manager@armka.local |
CLIENT_MANAGER | Краснодарский РФ | только ГК своего филиала, чтение |
4.5 Токен для Swagger и curl
TOKEN=$(curl -s -X POST localhost:8000/api/v1/dev/token -H 'Content-Type: application/json' \
-d '{"email":"ka_go@armka.local"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s localhost:8000/api/v1/me -H "Authorization: Bearer $TOKEN"
В Swagger: кнопка Authorize → вставить токен (без слова Bearer) → все запросы пойдут от этого пользователя. Чтобы проверить видимость разными ролями, получите второй токен другим email и переавторизуйтесь.
5. Как читать ответы
- Успех: 200/201, тело JSON. Удаление — 204 без тела.
- Нет токена или он плохой — 401. Нет права — 403 с текстом
недостаточно прав: требуется <КОД>. - Доменные ошибки — объект
{"detail": {"code": "...", "message": "..."}}: 404NOT_FOUND(в том числе для чужой записи — так задумано, чужое неотличимо от несуществующего), 409 конфликты (INN_EXISTS,LAST_ADMIN, …), 422 валидация (INVALID_INN,OFFICE_NOT_ALLOWED, …). - Ошибки валидации тела (не тот тип поля) — 422 со списком от pydantic.
- Списки —
{"items": [...], "total": N, "limit": 50, "offset": 0},limit≤ 200.
Полный каталог кодов — arm-core/README.md, раздел «Ошибки API».
6. Чек-лист сценариев
Отмечайте: ✅ ожидание совпало / ❌ баг (с номером issue) / ⚠️ вопрос. Пустые поля ожиданий — проверьте сами и зафиксируйте результат.
6.1 Администрирование (UI /admin, вход admin@armka.local)
| № | Шаги | Ожидание |
|---|---|---|
| A1 | Открыть «Система → Пользователи и роли» | таблица из 7 пользователей, вкладки «Пользователи» и «Матрица прав» |
| A2 | «Создать пользователя»: email qa1@example.com, ФИО, филиал, роль KA_RF |
пользователь появился; войти им в новой вкладке — получится, роли KA_RF |
| A3 | Создать пользователя с email qa1@EXAMPLE.com |
ошибка 409 EMAIL_EXISTS уведомлением |
| A4 | Создать пользователя с email x@corp.local |
ошибка 422 (домен .local отклоняется — известное ограничение) |
| A5 | У qa1@example.com сменить роли на KA_GO + EXPERT |
сохранилось; /me этого пользователя (новая вкладка) показывает обе роли |
| A6 | Снять роль ADMIN у admin@armka.local (у себя), пока он единственный админ |
409 LAST_ADMIN, роли не изменились |
| A7 | Выдать роль ADMIN пользователю qa1@example.com, затем снять ADMIN у себя |
теперь разрешено |
| A8 | Деактивировать единственного активного админа | 409 LAST_ADMIN |
| A9 | Матрица прав: роли EXPERT добавить EXPORT, сохранить |
/me эксперта содержит EXPORT |
| A10 | Матрица прав: снять USERS_ADMIN у ADMIN, когда других админов нет |
409 LAST_ADMIN |
| A11 | Войти expert@armka.local, открыть /admin напрямую |
«Нет доступа», пункт меню скрыт, запросов к /admin/* нет (вкладка Network) |
| A12 | После правки своих ролей обновить страницу | профиль в шапке обновился только после перезагрузки (известное ограничение) |
6.2 Справочники (Swagger /docs)
| № | Пользователь | Шаги | Ожидание |
|---|---|---|---|
| D1 | ka_go | POST /directories/company-groups {"name":"ГК Тест ГО"} |
201, code вида GK-000001, base_regional_office_id пустой |
| D2 | ka_rf | POST /directories/company-groups {"name":"ГК Тест РФ"} без филиала |
201, филиал подставлен Ростовский |
| D3 | ka_rf | то же с base_regional_office_id Краснодарского РФ (id из GET /directories/regional-offices) |
422 OFFICE_NOT_ALLOWED |
| D4 | ka_rf | GET /directories/company-groups |
только «ГК Тест РФ», total = 1 (ГК ГО не видна) |
| D5 | ka_rf | GET /directories/company-groups/{id ГК Тест ГО} |
404, тело такое же, как для случайного uuid |
| D6 | ka_go | GET /directories/company-groups |
обе ГК |
| D7 | ka_go | PATCH ГК: сменить имя |
200; GET .../history — одна запись с прежним именем и valid_to |
| D8 | ka_go | POST /directories/legal-entities ИП с ИНН из 10 цифр |
422 INVALID_INN |
| D9 | ka_go | ЮЛ {"inn":"7700000001","entity_type":"ЮЛ","name":"ООО Альфа","company_group_id":<ГК ГО>} |
201 |
| D10 | ka_go | повторно тот же ИНН | 409 INN_EXISTS |
| D11 | ka_go | ЮЛ без company_group_id |
201, в списке outside_group = true |
| D12 | ka_go | POST .../legal-entities/{id}/aliases {"alias":"ООО «Альфа»"} |
201, normalized_alias = альфа |
| D13 | ka_go | тот же алиас другому ЮЛ | 409 ALIAS_TAKEN с legal_entity_id владельца |
| D14 | ka_go | POST /directories/company-groups/{ГК}/related-groups kind gsk, затем gsz |
201 оба, коды GSK-…, GSZ-… |
| D15 | ka_go | добавить ЮЛ «Альфа» в ГСК и в ГСЗ той же ГК | оба 201 |
| D16 | ka_go | создать вторую ГСК в той же ГК и добавить туда «Альфа» | 409 MEMBER_OF_OTHER_GROUP |
| D17 | ka_go | добавить в ГСК ЮЛ из другой ГК | 422 DIFFERENT_COMPANY_GROUP |
| D18 | ka_go | POST .../legal-entities/{Альфа}/move в другую ГК, effective_date сегодня |
200; членства закрыты (GET .../legal-entities/{id} — memberships пуст); история MOVE_TO_GROUP |
| D19 | ka_go | move с effective_date вчера |
422 |
| D20 | ka_go | POST .../withdraw {"event":"CLOSE","effective_date":сегодня} |
close_date заполнен, company_group_id пуст |
| D21 | admin | DELETE /directories/related-groups/{id непустой} |
409 GROUP_NOT_EMPTY; пустой — 204 |
| D22 | ka_go | DELETE /directories/related-groups/{id пустой} |
403 (право DIRECTORY_DELETE только у ADMIN — так задумано) |
| D23 | expert | любой POST в справочники |
403 |
| D24 | любой | GET /directories/company-groups?limit=201 |
422 |
6.3 Импорт из ЕЦП CRM (пользователь admin, curl или Swagger)
Файл: fixtures/directories/ecp-export-russkiy-biskvit.xlsx (21 строка). Выполняйте на свежем стенде или после
сброса (раздел 8), иначе счётчики будут другими.
| № | Шаги | Ожидание |
|---|---|---|
| I1 | POST /directories/import с dry_run=true |
200; rows 21, groups_created 1, offices_created 1, entities_created 20, memberships_created 2, errors 1 (строка типа ФЛ → UNSUPPORTED_TYPE); в БД и справочниках ничего не появилось |
| I2 | тот же файл dry_run=false |
200, тот же отчёт; появились ГК «Русский Бисквит», филиал «Санкт-Петербургский РФ», 20 ЮЛ/ИП, одна ГСК с двумя членами |
| I3 | ещё раз dry_run=false, on_existing=skip |
все строки LE_EXISTS/GK_EXISTS, число записей не изменилось |
| I4 | csv без колонки «Тип» | 422 MISSING_COLUMNS, в ответе список недостающих колонок |
| I5 | файл в кодировке cp1251 (сохранить csv из Excel «CSV (разделители — запятые)») | 422 UNSUPPORTED_FILE |
| I6 | пользователем ka_go |
403 |
| I7 | полная выгрузка docs/source/biq/ecp-export-lagal-entity.xlsx, dry_run=true |
200 за несколько секунд; rows 8400, offices_created 53; есть коды UNSUPPORTED_TYPE, INVALID_INN, DUPLICATE_IN_FILE |
ADMIN=$(curl -s -X POST localhost:8000/api/v1/dev/token -H 'Content-Type: application/json' \
-d '{"email":"admin@armka.local"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s -X POST localhost:8000/api/v1/directories/import -H "Authorization: Bearer $ADMIN" \
-F file=@../fixtures/directories/ecp-export-russkiy-biskvit.xlsx -F dry_run=true | python3 -m json.tool | head -40
6.4 Аудит (БД)
docker compose -f ../docs/dev/docker-compose.yml exec postgres \
psql -U armka -d armka -c "select occurred_at, action, section, object_type, changed_field from dkb.audit_event order by occurred_at desc limit 20"
| № | После чего | Ожидание |
|---|---|---|
| L1 | A2, A5, A9 | записи USER_CHANGE, USER_ROLE_CHANGE, ROLE_PERMISSION_CHANGE с before_value/after_value |
| L2 | D1, D7 | GROUP_CHANGE; смена филиала ГК — ASSIGNMENT_CHANGE |
| L3 | D9, D12, D18 | LEGAL_ENTITY_CHANGE, членства — MEMBERSHIP_CHANGE |
| L4 | I2 | одна запись DIRECTORY_IMPORT с отчётом в after_value; после I1 (dry_run) записи нет |
| L5 | A6, D10 (отказы) | записей аудита нет |
6.5 Негатив и границы
| № | Шаги | Ожидание |
|---|---|---|
| N1 | запрос без заголовка Authorization |
401 |
| N2 | токен для несуществующего email (nobody@armka.local) через POST /dev/token |
404 пользователь не найден или отключён, токен не выдан |
| N3 | тело PATCH ГК с неизвестным полем |
422 (поля вне схемы запрещены) |
| N4 | импорт файла больше 20 МБ | 413 |
7. Известные ограничения (не заводить как баги)
- UI только для администрирования; справочники — через API.
- Вход без пароля, открытый
/docs, CORS*— dev-режим. - Email
*.localчерез API/UI отклоняется, при этом dev-пользователи именно такие (созданы seed напрямую). - Удалять алиасы и пустые ГСК/ГСЗ может только ADMIN (право
DIRECTORY_DELETE). - Граница «дата перевода не раньше сегодня» считается по UTC: до 03:00 МСК «сегодня» ещё вчера. Будет исправлено.
- Повторный
make seed-devвозвращает права, снятые администратором в матрице. - Профиль в шапке не перечитывается после правки собственных ролей.
- В консоли браузера сообщение
Store does not have a valid reducer— каркас, не влияет. - Таблица пользователей показывает первые 200.
- При импорте ИНН, записанный в Excel числом с ведущим нулём, теряет ноль и даёт
INVALID_INN.
Полный список отложенных находок — Gitea RSHB/arm-core, issue «Follow-ups from #5».
8. Сброс стенда
docker compose -f ../docs/dev/docker-compose.yml down -v # БД и бакет с нуля
make up && docker compose -f ../docs/dev/docker-compose.yml up -d --wait postgres nats minio
make migrate && make seed-dev
Бэкенд и фронт перезапускать не нужно, но сессии в браузере лучше начать заново.
9. Как заводить баги
Gitea, репозиторий RSHB/arm-core (для документации — RSHB/arm-ka-docs). Один дефект — одна issue.
Шаблон:
Срез: 1 / Сценарий: D16 (номер из гайда, если есть)
Пользователь: ka_go@armka.local (роль KA_GO)
Шаги: 1. … 2. …
Ожидание: 409 MEMBER_OF_OTHER_GROUP
Факт: 500 (тело ответа ниже)
Запрос: метод, путь, тело (без токена)
Ответ: статус и тело
Лог бэкенда: строки с трейсбэком, если есть
Окружение: ОС, версии docker/node, коммит arm-core (`git rev-parse --short HEAD`)
Вопросы по ожидаемому поведению (не ясно, баг ли) — тоже issue, с пометкой «вопрос» в заголовке.
10. Типичные проблемы при запуске
| Симптом | Причина и решение |
|---|---|
make up → порт занят |
локальный PostgreSQL/MinIO; остановить или сменить *_PORT в docs/dev/.env и те же порты в arm-core/api/.env |
docker compose up --wait без списка сервисов возвращает 1 |
нормально из-за minio-init; используйте команду со списком из 4.1 |
make migrate → connection refused |
PostgreSQL ещё не healthy; подождать и повторить |
make venv не находит Python 3.11 |
uv скачает его сам при наличии интернета; проверьте uv python list |
| Фронт открылся, но после входа пусто или ошибки сети | забыты REACT_APP_API_URL/REACT_APP_ROOT_PATH при npm run start |
| Страница «Вход выполняется через РАИСА» | это production-сборка; запускайте npm run start, а не build |
| Импорт падает на записи файла в S3 | MinIO не поднят или не создан бакет: docker compose logs minio-init |
/healthcheck показывает nats: down |
NATS не поднят; на тестирование Среза 1 не влияет |
Подробнее об инфраструктуре — docs/dev/README.md, о бэкенде — arm-core/README.md.