334 lines
17 KiB
Markdown
334 lines
17 KiB
Markdown
# Дизайн стабилизации quality gate, API-контракта и документации
|
||
|
||
## Статус
|
||
|
||
Одобрено для спецификации 2026-06-14.
|
||
|
||
## PRD
|
||
|
||
### Проблема
|
||
|
||
В MoexVibe накопился координационный технический долг между тестами, сгенерированными контрактами
|
||
и документацией. Приложение всё ещё собирается, но стандартный backend quality gate сейчас нельзя
|
||
считать надёжным:
|
||
|
||
- `npm run lint` падает из-за неиспользуемой переменной в backend-тесте.
|
||
- `npm run test:backend` падает, потому что часть backend-тестов обращается к живому MOEX API через
|
||
настоящий `MoexClientService`.
|
||
- Ошибки live MOEX-запросов проявляются как Vitest `DataCloneError`, потому что `AxiosError`
|
||
содержит функции в конфигурации запроса, которые нельзя клонировать между worker'ами.
|
||
- backend Swagger JSON по `/api/docs-json` и `apps/frontend/src/api/types.ts` не отражают актуальные эндпоинты
|
||
`auth`, `portfolios` и `securities/screener`.
|
||
- README, AGENTS и страницы Docusaurus местами описывают старое состояние репозитория.
|
||
- `npm run build:docs` успешно генерирует статические файлы, но выводит предупреждения Docusaurus о
|
||
broken link на `/`.
|
||
|
||
Из-за этого следующие задачи делать медленнее: разработчику неочевидно, какой вывод команд важен,
|
||
какой API-контракт актуален и почему backend-тесты падают: из-за поведения приложения или из-за
|
||
сетевой зависимости.
|
||
|
||
### Цели
|
||
|
||
1. Сделать стандартные проверки детерминированными и пригодными для локальной разработки и CI.
|
||
2. Сохранить live MOEX-проверки, но вынести их из стандартного unit-test пути.
|
||
3. Вернуть понятный источник правды для OpenAPI-контракта.
|
||
4. Перегенерировать или синхронизировать frontend OpenAPI-типы с текущими backend routes.
|
||
5. Привести README, AGENTS и Docusaurus-документацию к текущему состоянию репозитория.
|
||
6. Убрать actionable предупреждения Docusaurus о broken links из `npm run build:docs`.
|
||
|
||
### Не входит в задачу
|
||
|
||
- Пользовательские feature-изменения.
|
||
- Глубокий рефакторинг `PortfolioService` или `MoexClientService`.
|
||
- Переход frontend API-клиента на полностью сгенерированный клиент.
|
||
- Redis, изменение схемы БД, redesign auth-модели или portfolio analytics.
|
||
- Миграция CI-провайдера.
|
||
|
||
## Текущие находки
|
||
|
||
### Проходящие проверки
|
||
|
||
- `npm run test:frontend` проходит: 22 файла, 95 тестов.
|
||
- `npm run format:check` проходит.
|
||
- `npm run build:backend` проходит.
|
||
- `npm run build:frontend` проходит.
|
||
- `npm run build:docs` успешно генерирует статические файлы.
|
||
|
||
### Падающие или шумные проверки
|
||
|
||
- `npm run lint` падает в `apps/backend/src/modules/securities/screener.service.spec.ts`, потому
|
||
что `moexClient` присваивается, но не используется.
|
||
- `npm run test:backend` падает с unhandled Vitest errors. Затронутые specs создают настоящий
|
||
`MoexClientService` и ходят в MOEX:
|
||
- `apps/backend/src/modules/moex-client/moex-client.service.spec.ts`
|
||
- `apps/backend/src/modules/securities/securities.service.spec.ts`
|
||
- `apps/backend/src/modules/candles/candles.service.spec.ts`
|
||
- похожие service specs для shares и bonds тоже зависят от live MOEX-доступа.
|
||
- `npm run build:docs` предупреждает, что многие страницы ссылаются на `/`.
|
||
|
||
### Устаревшая документация и контрактные артефакты
|
||
|
||
- `AGENTS.md` говорит, что `npm run lint` проверяет только backend, а frontend-тестов нет.
|
||
- `apps/docs/docs/development/testing.md` говорит, что frontend-тестов нет.
|
||
- `apps/docs/docs/development/commands.md` не описывает `test:frontend`, docs scripts и frontend
|
||
lint.
|
||
- `apps/docs/docs/frontend/routes.md` не описывает `/login`, `/register`, `/profile`,
|
||
`/portfolios`, `/portfolios/:id` и `/screener`.
|
||
- `apps/docs/docs/frontend/overview.md` не описывает auth, portfolio, screener и test-директории.
|
||
- `apps/docs/docs/backend/api.md` не содержит portfolio и screener endpoints.
|
||
- `apps/docs/docs/backend/portfolio.md` документирует `PATCH /api/v1/portfolios/:id/patch`, хотя
|
||
controller реализует `PATCH /api/v1/portfolios/:id`.
|
||
- backend Swagger JSON по `/api/docs-json` и `apps/frontend/src/api/types.ts` содержат только ранние paths для
|
||
health, search, shares, bonds и candles.
|
||
|
||
## Доменная модель
|
||
|
||
### Quality Gate
|
||
|
||
Quality gate: это повторяемая команда, которую разработчик может запускать без внешних зависимостей,
|
||
если сама команда явно не говорит обратного.
|
||
|
||
Стандартные проверки:
|
||
|
||
- `npm run lint`
|
||
- `npm run test:backend`
|
||
- `npm run test:frontend`
|
||
- `npm run build:backend`
|
||
- `npm run build:frontend`
|
||
- `npm run build:docs`
|
||
- `npm run format:check`
|
||
|
||
Opt-in проверки:
|
||
|
||
- Live MOEX integration checks. Они могут требовать network access и не должны запускаться в
|
||
стандартных unit tests или CI jobs без явного запроса.
|
||
|
||
### Источник API-контракта
|
||
|
||
Авторитетный backend contract: NestJS Swagger document, который генерируется из controllers и DTO
|
||
decorators по `/api/docs-json`.
|
||
|
||
Сгенерированные или синхронизированные артефакты:
|
||
|
||
- `/api/docs-json`: live Swagger JSON, канонический machine-readable contract.
|
||
- `apps/frontend/src/api/types.ts`: сгенерированные TypeScript path и schema types.
|
||
- Docusaurus API pages: поясняющая документация, но не канонический machine contract.
|
||
|
||
### Источник документации
|
||
|
||
Документация должна описывать текущее состояние репозитория, а не исторический план реализации.
|
||
Superpowers specs и plans остаются историей проекта. README, AGENTS и Docusaurus pages являются
|
||
актуальной onboarding-поверхностью.
|
||
|
||
## ADR
|
||
|
||
### Решение
|
||
|
||
Разделить backend tests на детерминированные unit tests и opt-in live MOEX integration tests.
|
||
|
||
### Обоснование
|
||
|
||
Стандартная backend test command сейчас смешивает unit-поведение и доступность внешней сети. Это
|
||
делает failures неоднозначными и порождает шумные Vitest serialization errors, когда Axios
|
||
возвращает rejection с non-cloneable configuration fields. Unit tests должны проверять логику
|
||
приложения на контролируемых fixtures. Live MOEX tests полезны, но должны быть отдельной явно
|
||
названной командой с понятным требованием к окружению.
|
||
|
||
### Последствия
|
||
|
||
- `npm run test:backend` становится стабильной offline-командой.
|
||
- Live MOEX coverage остаётся доступным через отдельную integration command.
|
||
- Часть существующих specs изменится с "real MOEX smoke test" на "service behavior with mocked
|
||
`MoexClientService`".
|
||
- Contract drift станет видимым, потому что OpenAPI snapshots и frontend generated types будут
|
||
обновлены в рамках этой работы.
|
||
|
||
## Backend Architecture
|
||
|
||
### Граница unit tests
|
||
|
||
Service tests для `SharesService`, `BondsService`, `CandlesService` и `SecuritiesService` должны
|
||
mock'ать `MoexClientService` и `CacheService`.
|
||
|
||
Mocked data должны проверять поведение, важное для MoexVibe:
|
||
|
||
- нормализованные share data возвращаются из cached или fetched MOEX client data;
|
||
- нормализованные bond data корректно обрабатывают отсутствующие market fields как nullable values;
|
||
- candles мапятся в public response shape;
|
||
- search и screener используют детерминированные fixture rows;
|
||
- cache metadata остаётся представленной через `{ data, meta }`, где этого требуют service
|
||
contracts.
|
||
|
||
### Граница live integration tests
|
||
|
||
Live MOEX checks должны быть изолированы в `*.integration.spec.ts` files или эквивалентном явном
|
||
test path. Они должны запускаться только через отдельную команду, например
|
||
`npm run test:integration -w apps/backend`, и должны документировать, что для них требуется network
|
||
access.
|
||
|
||
Integration command не должна входить в стандартный `npm run test:backend`.
|
||
|
||
### OpenAPI decorators
|
||
|
||
Существующие controllers должны отдавать достаточно Swagger metadata для generated path types:
|
||
|
||
- Auth routes: register, login, refresh, logout, me, profile update.
|
||
- Securities routes: search и screener.
|
||
- Portfolio routes: list, create, detail, update, delete, position mutations, analytics.
|
||
- Существующие shares, bonds, candles и health routes.
|
||
|
||
В реализации нужно предпочитать существующие DTO и response DTO. Если для response нет DTO и
|
||
добавление полного DTO слишком раздувает первый проход, можно использовать минимальные response
|
||
decorators без изменения runtime behavior.
|
||
|
||
## Frontend Architecture
|
||
|
||
### Generated Types
|
||
|
||
`apps/frontend/src/api/types.ts` должен быть перегенерирован из текущего backend Swagger JSON после
|
||
того, как backend Swagger metadata будет покрывать актуальные routes.
|
||
|
||
Существующие hand-written `responses.ts` и API wrapper modules остаются на месте в этом эпике. Цель:
|
||
свежесть контракта, а не полный rewrite клиента.
|
||
|
||
### Tests
|
||
|
||
Frontend tests уже существуют и проходят. Этот эпик не должен переписывать frontend testing
|
||
architecture. Если API response types изменятся, frontend tests нужно обновлять только там, где
|
||
перегенерированный contract выявит реальное несоответствие.
|
||
|
||
## OpenAPI Contract Scope
|
||
|
||
Синхронизированный contract должен включать минимум эти paths под `/api/v1`:
|
||
|
||
- `GET /health`
|
||
- `POST /auth/register`
|
||
- `POST /auth/login`
|
||
- `POST /auth/refresh`
|
||
- `POST /auth/logout`
|
||
- `GET /auth/me`
|
||
- `PATCH /auth/me`
|
||
- `GET /securities/search`
|
||
- `GET /securities/screener`
|
||
- `GET /securities/shares/{secid}`
|
||
- `GET /securities/shares/{secid}/marketdata`
|
||
- `GET /securities/shares/{secid}/dividends`
|
||
- `GET /securities/shares/{secid}/history`
|
||
- `GET /securities/shares/{secid}/candles`
|
||
- `GET /securities/bonds/{secid}`
|
||
- `GET /securities/bonds/{secid}/marketdata`
|
||
- `GET /securities/bonds/{secid}/history`
|
||
- `GET /securities/bonds/{secid}/candles`
|
||
- `GET /portfolios`
|
||
- `POST /portfolios`
|
||
- `GET /portfolios/{id}`
|
||
- `PATCH /portfolios/{id}`
|
||
- `DELETE /portfolios/{id}`
|
||
- `POST /portfolios/{id}/positions`
|
||
- `PATCH /portfolios/{id}/positions/{positionId}`
|
||
- `DELETE /portfolios/{id}/positions/{positionId}`
|
||
- `GET /portfolios/{id}/analytics`
|
||
|
||
## Documentation Architecture
|
||
|
||
### README
|
||
|
||
Обновить README так, чтобы quickstart и test sections упоминали backend, frontend и docs
|
||
workspaces.
|
||
|
||
### AGENTS
|
||
|
||
Обновить AGENTS:
|
||
|
||
- `apps/docs` является частью workspace.
|
||
- `npm run lint` запускает backend и frontend lint.
|
||
- frontend tests существуют.
|
||
- pre-commit checks существуют через Husky и lint-staged.
|
||
- CI существует в `.gitea/workflows/ci.yml`.
|
||
|
||
### Docusaurus
|
||
|
||
Обновить текущие onboarding pages:
|
||
|
||
- development commands;
|
||
- testing;
|
||
- code generation;
|
||
- frontend overview;
|
||
- frontend routes;
|
||
- frontend API client;
|
||
- backend API;
|
||
- backend portfolio.
|
||
|
||
Исправить Docusaurus config или docs links так, чтобы `npm run build:docs` больше не сообщал о
|
||
broken links на `/`. Отдельное update-check warning про permissions в `/Users/ksv741/.config`
|
||
является внешним к репозиторию и в этот эпик не входит.
|
||
|
||
## Этапы реализации
|
||
|
||
### Этап 1: стабилизировать стандартные проверки
|
||
|
||
1. Исправить неиспользуемую backend test variable, которая ломает lint.
|
||
2. Перевести стандартные backend service specs с live MOEX calls на mocked dependencies.
|
||
3. Вынести или добавить live MOEX smoke coverage под opt-in integration command.
|
||
4. Проверить `npm run lint` и `npm run test:backend`.
|
||
|
||
### Этап 2: обновить contract artifacts
|
||
|
||
1. Добавить или завершить Swagger metadata для актуальных routes.
|
||
2. Перегенерировать `apps/frontend/src/api/types.ts`.
|
||
3. Проверить `/api/docs-json` и синхронизировать `apps/frontend/src/api/types.ts` с текущим contract.
|
||
4. Проверить, что generated paths включают auth, screener и portfolio routes.
|
||
|
||
### Этап 3: обновить документацию
|
||
|
||
1. Обновить README и AGENTS.
|
||
2. Обновить Docusaurus development, frontend, backend и portfolio pages.
|
||
3. Исправить Docusaurus broken `/` link warning.
|
||
4. Проверить `npm run build:docs`.
|
||
|
||
### Этап 4: полная проверка
|
||
|
||
Запустить:
|
||
|
||
```bash
|
||
npm run lint
|
||
npm run test:backend
|
||
npm run test:frontend
|
||
npm run build:backend
|
||
npm run build:frontend
|
||
npm run build:docs
|
||
npm run format:check
|
||
```
|
||
|
||
## Acceptance Criteria
|
||
|
||
- `npm run lint` завершается с exit code 0.
|
||
- `npm run test:backend` завершается с exit code 0 без live MOEX/network dependency.
|
||
- `npm run test:frontend` завершается с exit code 0.
|
||
- `npm run build:backend` завершается с exit code 0.
|
||
- `npm run build:frontend` завершается с exit code 0.
|
||
- `npm run build:docs` завершается с exit code 0 и больше не сообщает о Docusaurus broken links на
|
||
`/`.
|
||
- `npm run format:check` завершается с exit code 0.
|
||
- `apps/frontend/src/api/types.ts` содержит актуальные auth, screener и portfolio paths.
|
||
- `/api/docs-json` содержит актуальные auth, screener и portfolio paths.
|
||
- README, AGENTS и Docusaurus docs больше не утверждают, что frontend tests отсутствуют.
|
||
- Portfolio docs используют `PATCH /api/v1/portfolios/:id`, что соответствует controller.
|
||
|
||
## Риски
|
||
|
||
- Swagger decorators могут показать DTO gaps, которые раньше были скрыты hand-written frontend
|
||
types. Первый проход должен оставаться сфокусированным на свежести paths и schemas; redesign
|
||
API-клиента откладывается.
|
||
- Live MOEX tests могут по-прежнему падать в окружениях с network restrictions. Это допустимо только
|
||
для opt-in integration command, но не для default backend test command.
|
||
- Regenerating OpenAPI artifacts может дать большой diff. Generated changes нужно ревьюить отдельно
|
||
от hand-written docs changes.
|
||
|
||
## Self-Review спецификации
|
||
|
||
- Placeholder scan: placeholder markers и незавершённые sections отсутствуют.
|
||
- Internal consistency: default tests остаются offline, live MOEX checks являются opt-in.
|
||
- Scope check: scope ограничен quality gates, contract snapshots и актуальностью docs.
|
||
- Ambiguity check: acceptance criteria называют конкретные commands и contract paths.
|