mirror of
https://github.com/kamask-dev/pchelpmaster.git
synced 2026-11-11 05:49:38 +00:00
- add tsconfig.tests.json and run tsc in test:api - move vitest ProvidedContext types to provided-context.d.ts - rename vitest.api.config.ts to .mts - kill signal-test fixture with SIGTERM first so cleanup still runs - add tests: no files in pb_migrations, independent user/guest clients - docs: drop superuser env vars from tests in README, note tsc in testing.md Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EvmtStx737hu8gXe15e55W
63 lines
6.9 KiB
Markdown
63 lines
6.9 KiB
Markdown
# Тестирование
|
||
|
||
Разработка ведётся по TDD: тесты пишутся до реализации.
|
||
|
||
## Уровни тестов
|
||
|
||
| Уровень | Что проверяет | Где лежит | Команда |
|
||
| --------------------- | -------------------------------------------------------------------------- | --------------------------------------------- | ----------------------- |
|
||
| **API** (e2e бэкенда) | схема, миграции, правила доступа, изоляция пользователей, хуки PocketBase | `tests/api/**/*.api.spec.ts` | `pnpm test:api` |
|
||
| **Unit** | доменные функции, компоненты и сервисы с фейками вместо PocketBase | рядом с кодом, `src/**/*.spec.ts` | `pnpm test` |
|
||
| **Integration** | PocketBase-реализации репозиториев и сервисов против настоящего PocketBase | рядом с кодом, `src/**/*.integration.spec.ts` | `pnpm test:integration` |
|
||
| **E2E** | пользовательские сценарии в браузере, SSR, доступность (axe) | `tests/e2e/**/*.e2e.ts` | `pnpm e2e` |
|
||
| **E2E prod** | сценарии на production-сборке, включая изоляцию данных при SSR | `tests/e2e/` (проект `ssr-prod`) | `pnpm e2e:prod` |
|
||
|
||
Команды появляются по мере того, как настраиваются соответствующие стенды.
|
||
|
||
`pnpm test:api` сначала проверяет типы тестового кода (`tsconfig.tests.json`: `tests/**` и `vitest.api.config.mts`), затем запускает Vitest.
|
||
|
||
## Контрактные тесты репозиториев
|
||
|
||
У каждого репозитория две реализации: PocketBase (`data/`) и фейк в памяти (`testing/`). Чтобы фейк не расходился с реальностью, поведение описывается **один раз**:
|
||
|
||
```ts
|
||
// tags/testing/tag-repository.contract.ts
|
||
export function describeTagRepositoryContract(
|
||
setup: () => Promise<{ repoA: TagRepository; repoB: TagRepository }>,
|
||
) {
|
||
describe('контракт TagRepository', () => {
|
||
it('не показывает пользователю B метки пользователя A', async () => {
|
||
/* ... */
|
||
});
|
||
});
|
||
}
|
||
```
|
||
|
||
и вызывается дважды: в `in-memory-tag-repository.spec.ts` (быстро, в `pnpm test`) и в `pocketbase-tag-repository.integration.spec.ts` (на тестовом PocketBase, в `pnpm test:integration`).
|
||
|
||
## Тестовый PocketBase
|
||
|
||
API-, integration- и e2e-тесты работают с **отдельным временным экземпляром** PocketBase, а не с `pocketbase/pb_data`:
|
||
|
||
1. Создаётся временная папка данных (в системной temp-папке).
|
||
2. Создаётся суперпользователь (`pocketbase superuser upsert`) со сгенерированными email и паролем: инстанс одноразовый, поэтому `.env` тестам не нужен.
|
||
3. Запускается `pocketbase serve` на свободном порту `127.0.0.1` с `--dir <временная папка> --migrationsDir pocketbase/pb_migrations --hooksDir pocketbase/pb_hooks --automigrate=false`; миграции применяются при старте.
|
||
4. Ожидание готовности по `GET /api/health`.
|
||
5. После прогона процесс останавливается, папка удаляется.
|
||
|
||
Код запуска один на все уровни: `tests/support/pocketbase-server.ts` (`startPocketBase(): Promise<{ url, dataDir, superuser, stop }>`). В API-тестах URL и суперпользователь доступны через `inject('pbUrl')` и `inject('pbSuperuser')`.
|
||
|
||
Стенд убирает за собой процесс и папку после успешного прогона, после падения (в том числе неудачного старта), по `SIGINT`/`SIGTERM` (Ctrl+C) и при выходе процесса. Предел: `SIGKILL` процесса-владельца перехватить нельзя — тогда PocketBase может продолжить работу, а папка `pchelpcrm-pb-*` остаться в temp.
|
||
|
||
## Правила написания тестов
|
||
|
||
- **Каждый тест создаёт своих пользователей** с уникальными email (`user-<случайная строка>@test.local`). Тесты не зависят друг от друга и от порядка, поэтому могут идти параллельно и не нуждаются в очистке.
|
||
- Хелперы (`tests/support/`): `createUser()`, `asUser(user)`, `asGuest()`, `asSuperuser()` возвращают готовый экземпляр PocketBase SDK.
|
||
- **Изоляцию проверяем всегда двумя пользователями**: A создаёт данные, B пытается их прочитать, найти через list/filter, изменить, удалить, сослаться на них. Плюс гость.
|
||
- Тест проверяет поведение, а не реализацию: статус ответа, тело, состояние после операции (перечитать суперпользователем).
|
||
- Unit-тесты компонентов используют фейки из `<feature>/testing/` и не обращаются к сети.
|
||
- Компонентные тесты ищут элементы по ролям и подписям (как пользователь и скринридер), а не по CSS-классам.
|
||
- E2E-тесты: каждый сценарий регистрирует своего пользователя через UI или API; на ключевых страницах выполняется проверка axe (`@axe-core/playwright`) без нарушений.
|
||
- Названия тестов на русском и описывают поведение: `it('не отдаёт гостю чужую метку')`.
|
||
- Пока реализации нет, тесты должны компилироваться: для этого создаются минимальные заглушки контрактов (типы, абстрактные классы, методы с `throw new Error('Not implemented')`). Новые тесты падают из-за невыполненных ожиданий, а не из-за ошибок импорта или стенда.
|