# 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 ```