feat: добавил новые юнит-тесты для сервисов и маппинга
Backend CI / build-and-test (push) Successful in 48s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 5s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 24s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Failing after 14s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 2s

This commit is contained in:
2026-05-28 05:04:43 +03:00
parent cce7bea12f
commit ef2fd39508
6 changed files with 651 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# Backend unit-тесты
## Назначение
Unit- и service-тесты backend проверяют бизнес-логику без запуска HTTP API:
- доменные правила записи на лекции;
- преобразование сущностей в DTO;
- фильтрацию, пагинацию и связи курсов;
- дерево тегов;
- управление ролями и профилями пользователей.
Security-тесты авторизации находятся в том же тестовом проекте, но это отдельный интеграционный набор: они запускают API через `WebApplicationFactory` и проверяют HTTP-доступ к endpoint-ам.
## Где лежат файлы
- [EnrollmentSlotPolicyTests.cs](../backend/UniVerse.Api.Tests/Domain/EnrollmentSlotPolicyTests.cs) - правила лимита активных записей по уровню.
- [MappingExtensionsTests.cs](../backend/UniVerse.Api.Tests/Application/MappingExtensionsTests.cs) - маппинг доменных сущностей в DTO.
- [CourseServiceTests.cs](../backend/UniVerse.Api.Tests/Courses/CourseServiceTests.cs) - фильтры, пагинация и теги курсов.
- [TagServiceTests.cs](../backend/UniVerse.Api.Tests/Tags/TagServiceTests.cs) - фильтры тегов и построение дерева.
- [UserServiceTests.cs](../backend/UniVerse.Api.Tests/Users/UserServiceTests.cs) - статистика, роли, профили и список пользователей.
- [EndpointAuthorizationTests.cs](../backend/UniVerse.Api.Tests/Authorization/EndpointAuthorizationTests.cs) - security-тесты ролевого доступа к API.
- [ApiWebApplicationFactory.cs](../backend/UniVerse.Api.Tests/Helpers/ApiWebApplicationFactory.cs) - тестовый запуск API.
- [TestJwtFactory.cs](../backend/UniVerse.Api.Tests/Helpers/TestJwtFactory.cs) - генерация JWT для ролей в security-тестах.
Тестовый проект: [UniVerse.Api.Tests.csproj](../backend/UniVerse.Api.Tests/UniVerse.Api.Tests.csproj).
## Тестовый стек
- `xUnit` - test runner и assertions.
- `NSubstitute` - mock-объекты для сервисных зависимостей.
- `Microsoft.EntityFrameworkCore.InMemory` - изолированная InMemory БД для service-тестов.
Каждый service-тест создает отдельный `AppDbContext` с уникальным именем базы через `Guid.NewGuid()`, чтобы данные разных тестов не пересекались.
## Что покрыто
### EnrollmentSlotPolicy
Проверяется, что `GetLimitForLevel` выбирает последний подходящий threshold:
- уровни ниже первого правила получают базовый лимит;
- уровни между threshold используют предыдущий лимит;
- уровни выше последнего threshold используют максимальный лимит;
- публичный список `Rules` остается в ожидаемом порядке.
### MappingExtensions
Проверяется стабильность DTO-маппинга:
- роли пользователя сортируются одинаково в `UserDto`, `CurrentUserDto` и `UserAuthDto`;
- лекции корректно считают записи и используют fallback для отсутствующих navigation properties;
- отзывы переносят поля LLM-анализа;
- дерево тегов маппится рекурсивно.
### CourseService
Проверяется поведение без HTTP-слоя:
- совместная работа фильтров `Search`, `IsSynced`, `TagId`;
- корректные `TotalCount`, `Page`, `PageSize`, `TotalPages`;
- добавление связи курс-тег;
- ошибки при повторной связи или отсутствующем курсе/теге.
### TagService
Проверяется:
- фильтрация по `TagType` и `ParentId`;
- сортировка по имени;
- запрет создания дочернего тега без существующего родителя;
- построение вложенного дерева тегов.
### UserService
Проверяется:
- статистика пользователя, прогресс уровня и лимиты записей;
- `SetRolesAsync` удаляет дубли ролей;
- пустой набор ролей отклоняется;
- профили студента и преподавателя создаются и не дублируются;
- `GetAllAsync` фильтрует по поиску, активности и одиночной роли;
- пагинация пользователей идет в порядке `CreatedAt` по убыванию.
## Security-тесты авторизации
Security-тесты находятся в [EndpointAuthorizationTests.cs](../backend/UniVerse.Api.Tests/Authorization/EndpointAuthorizationTests.cs). Это интеграционные тесты, которые отправляют реальные HTTP-запросы в тестовый API через `ApiWebApplicationFactory`.
Они проверяют не бизнес-результат endpoint-а, а сам факт прохождения или блокировки авторизации:
- анонимный запрос к защищенному endpoint-у получает `401 Unauthorized`;
- запрос с неподходящей ролью получает `403 Forbidden`;
- запрос с подходящей ролью не получает `401` или `403`;
- публичные endpoint-ы из `AnonymousEndpoints` доступны без JWT и не возвращают `401` от middleware авторизации.
Таблица защищенных endpoint-ов задается в методе `AuthenticatedEndpoints`. Каждый кейс описывает:
- человекочитаемое имя сценария;
- HTTP-метод;
- URL;
- роль, которая должна пройти авторизацию;
- роли, которые должны получить `403`;
- опциональное JSON-тело запроса.
Для endpoint-ов, доступных любой авторизованной роли, используется обычная тестовая роль, чаще `Student`, и пустой список запрещенных ролей. Для endpoint-ов с несколькими разрешенными ролями добавляется отдельный кейс на каждую разрешенную роль, например `Admin` и `Teacher`.
JWT для ролей создаются через `TestJwtFactory.BearerHeader(role)`. Это позволяет проверять backend-авторизацию без Microsoft OAuth flow и без реального входа пользователя.
## Как обновлять security-тесты
При добавлении или изменении API endpoint-а нужно обновить `EndpointAuthorizationTests`:
1. Если endpoint требует авторизации, добавьте его в `AuthenticatedEndpoints`.
2. Укажите правильную роль или отдельные кейсы для нескольких ролей.
3. Для role-specific endpoint-а заполните `forbidden` ролями, которые должны получать `403`.
4. Если endpoint публичный, добавьте его в `AnonymousEndpoints`.
5. Для `POST`, `PUT`, `PATCH` endpoint-ов добавьте минимальное валидное тело запроса, чтобы тест дошел до авторизации и не падал на model binding раньше времени.
Security-тест считается успешным для правильной роли, если ответ не `401` и не `403`. Это намеренно: после авторизации endpoint может вернуть `404`, `400`, `409` или другой доменный ответ из-за тестовых данных, и это не является ошибкой проверки доступа.
## Как запускать
Из корня репозитория:
```bash
dotnet test backend/UniVerse.sln --no-restore
```
## Как добавлять новые unit/service-тесты
1. Размещайте тесты рядом с проверяемой областью внутри `backend/UniVerse.Api.Tests`.
2. Для сервисов с EF используйте InMemory `AppDbContext` с уникальным именем базы.
3. Мокайте только внешние зависимости и соседние сервисы через `NSubstitute`.
4. Не запускайте `WebApplicationFactory`, если проверяется не HTTP/auth behavior.
5. Покрывайте не только успешный сценарий, но и доменные ошибки: `NotFoundException`, `ConflictException`, `ForbiddenException`.
## Текущий baseline
После добавления unit/service-тестов и с учетом существующих security-тестов полный backend test suite проходит:
```text
Passed: 303, Failed: 0, Skipped: 0
```