# Тестовая спецификация API
## Содержание
1. [GET /account/get](#1-get-accountget)
2. [POST /account/create](#2-post-accountcreate)
3. [DELETE /account/delete](#3-delete-accountdelete)
4. [GET /accounts](#4-get-accounts)
5. [GET /privilege/{userId}](#5-get-privilegeuserid)
6. [DELETE /account/{userId}](#6-delete-accountuserid)
7. [POST /account/manualdone](#7-post-accountmanualdone)
8. [POST /account/leadtarget](#8-post-accountleadtarget)
9. [PUT /account/leadtarget](#9-put-accountleadtarget)
10. [DELETE /account/leadtarget/{id}](#10-delete-accountleadtargetid)
11. [GET /account/leadtarget/{quizID}](#11-get-accountleadtargetquizid)
12. [POST /question/create](#12-post-questioncreate)
13. [POST /question/getList](#13-post-questiongetlist)
14. [PATCH /question/edit](#14-patch-questionedit)
15. [POST /question/copy](#15-post-questioncopy)
16. [POST /question/history](#16-post-questionhistory)
17. [DELETE /question/delete](#17-delete-questiondelete)
18. [POST /quiz/create](#18-post-quizcreate)
19. [POST /quiz/getList](#19-post-quizgetlist)
20. [PATCH /quiz/edit](#20-patch-quizedit)
21. [POST /quiz/copy](#21-post-quizcopy)
22. [POST /quiz/history](#22-post-quizhistory)
23. [DELETE /quiz/delete](#23-delete-quizdelete)
24. [PATCH /quiz/archive](#24-patch-quizarchive)
25. [POST /quiz/move](#25-post-quizmove)
26. [POST /quiz/template](#26-post-quiztemplate)
27. [POST /results/getResults/{quizId}](#27-post-resultsgetresultsquizid)
28. [DELETE /results/{resultId}](#28-delete-resultsresultid)
29. [PATCH /result/seen](#29-patch-resultseen)
30. [POST /results/{quizID}/export](#30-post-resultsquizidexport)
31. [GET /result/{resultID}](#31-get-resultresultid)
32. [POST /statistic/{quizID}/devices](#32-post-statisticquiziddevices)
33. [POST /statistic/{quizID}/general](#33-post-statisticquizidgeneral)
34. [POST /statistic/{quizID}/questions](#34-post-statisticquizidquestions)
35. [POST /statistic](#35-post-statistic)
36. [GET /statistics/{quizID}/pipelines](#36-get-statisticsquizidpipelines)
37. [GET /telegram/pool](#37-get-telegrampool)
38. [POST /telegram/create](#38-post-telegramcreate)
39. [DELETE /telegram/{id}](#39-delete-telegramid)
40. [POST /telegram/setCode](#40-post-telegramsetcode)
## 1. GET /account/get
### 1.1 Описание
Получение информации о текущем аккаунте пользователя.
### 1.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Возвращает детальную информацию об аккаунте в формате JSON
- Поддерживает стандартные HTTP коды ответа: 200, 401, 404, 500
### 1.3 Тестовые сценарии
#### 1.3.1 Успешное получение информации об аккаунте
**Предусловия:**
- Пользователь авторизован
- Аккаунт существует и активен
- JWT токен валиден
**Входные данные:**
- Метод: GET
- URL: /account/get
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект Account со следующими полями:
- id (string)
- user_id (string)
- created_at (date-time)
- privileges (object)
**Проверки:**
1. Все обязательные поля присутствуют в ответе
2. Формат даты created_at соответствует ISO 8601
3. Структура privileges соответствует схеме ShortPrivilege
4. Значения полей соответствуют данным в базе
#### 1.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 1.3.3 Проверка существования аккаунта
**Сценарий 3.1: Аккаунт не найден**
- Предусловие: Аккаунт удален или не существует
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
**Сценарий 3.2: Аккаунт деактивирован**
- Предусловие: Аккаунт помечен как deleted=true
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
#### 1.3.4 Проверка привилегий
**Сценарий 4.1: Аккаунт без привилегий**
- Предусловие: Аккаунт существует, но не имеет привилегий
- Ожидаемый результат: 200 OK
- Проверка: privileges = {}
**Сценарий 4.2: Аккаунт с множеством привилегий**
- Предусловие: Аккаунт имеет несколько активных привилегий
- Ожидаемый результат: 200 OK
- Проверка структуры и содержимого privileges
#### 1.3.5 Проверка производительности
**Сценарий 5.1: Время ответа**
- Проверка: время ответа < 500ms
- Проверка: стабильность времени ответа при повторных запросах
**Сценарий 5.2: Нагрузочное тестирование**
- Проверка: система выдерживает 100 последовательных запросов
- Проверка: отсутствие утечек памяти
#### 1.3.6 Проверка безопасности
**Сценарий 6.1: XSS уязвимости**
- Проверка: экранирование специальных символов в ответе
- Проверка: корректные заголовки безопасности
**Сценарий 6.2: CSRF защита**
- Проверка: наличие и валидность CSRF токена
- Проверка: корректная обработка невалидного CSRF токена
#### 1.3.7 Граничные случаи
**Сценарий 7.1: Очень длинные значения полей**
- Проверка: обработка полей с максимально допустимой длиной
- Проверка: корректное обрезание/валидация длинных значений
**Сценарий 7.2: Специальные символы в данных**
- Проверка: корректная обработка Unicode символов
- Проверка: обработка специальных символов в именах полей
#### 1.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 1.4 Особые моменты для тестирования
1. **Валидация JWT токена:**
- Проверка подписи токена
- Проверка срока действия
- Проверка структуры claims
2. **Кэширование:**
- Проверка правильности кэширования ответов
- Проверка инвалидации кэша при изменении данных
3. **Логирование:**
- Проверка записи всех важных событий
- Проверка корректности логов
4. **Мониторинг:**
- Проверка метрик производительности
- Проверка алертов при ошибках
### 1.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время ответа соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
## 2. POST /account/create
### 2.1 Описание
Создание нового аккаунта пользователя.
### 2.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Создает новый аккаунт в системе
- Поддерживает HTTP коды ответа: 200, 401, 409, 500
### 2.3 Тестовые сценарии
#### 2.3.1 Успешное создание аккаунта
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- Аккаунт с таким user_id еще не существует
**Входные данные:**
- Метод: POST
- URL: /account/create
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект Account со следующими полями:
- id (string)
- user_id (string)
- created_at (date-time)
- privileges (object)
**Проверки:**
1. Аккаунт успешно создан в базе данных
2. Все обязательные поля присутствуют в ответе
3. Формат даты created_at соответствует ISO 8601
4. Значения полей соответствуют переданным данным
#### 2.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 2.3.3 Проверка конфликтов
**Сценарий 3.1: Создание дублирующегося аккаунта**
- Предусловие: Аккаунт с таким user_id уже существует
- Ожидаемый результат: 409 Conflict
- Проверка сообщения об ошибке
**Сценарий 3.2: Попытка создания аккаунта с существующим ID**
- Предусловие: Передан ID существующего аккаунта
- Ожидаемый результат: 409 Conflict
- Проверка сообщения об ошибке
#### 2.3.4 Проверка валидации данных
**Сценарий 4.1: Отсутствие обязательных полей**
- Входные данные: пустой JSON объект
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 4.2: Некорректный формат данных**
- Входные данные: неверный формат полей
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
#### 2.3.5 Проверка безопасности
**Сценарий 5.1: SQL-инъекции**
- Входные данные: SQL-инъекция в полях
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 5.2: XSS-атаки**
- Входные данные: XSS-код в полях
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 2.3.6 Проверка производительности
**Сценарий 6.1: Время создания**
- Проверка: время создания аккаунта < 1s
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система выдерживает создание 100 аккаунтов подряд
- Проверка: отсутствие утечек памяти
#### 2.3.7 Граничные случаи
**Сценарий 7.1: Очень длинные значения полей**
- Входные данные: поля с максимально допустимой длиной
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
**Сценарий 7.2: Специальные символы**
- Входные данные: Unicode символы в полях
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
#### 2.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 2.4 Особые моменты для тестирования
1. **Валидация данных:**
- Проверка всех обязательных полей
- Проверка форматов данных
- Проверка уникальности user_id
2. **Транзакционность:**
- Проверка атомарности операции создания
- Проверка отката при ошибках
3. **Логирование:**
- Проверка записи создания аккаунта
- Проверка аудит-логов
4. **Мониторинг:**
- Проверка метрик создания аккаунтов
- Проверка алертов при ошибках
### 2.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время создания аккаунта соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
## 3. DELETE /account/delete
### 3.1 Описание
Удаление текущего аккаунта пользователя.
### 3.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Удаляет текущий аккаунт пользователя
- Поддерживает HTTP коды ответа: 200, 401, 500
### 3.3 Тестовые сценарии
#### 3.3.1 Успешное удаление аккаунта
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- Аккаунт существует и активен
**Входные данные:**
- Метод: DELETE
- URL: /account/delete
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект с полем accountId (string)
**Проверки:**
1. Аккаунт успешно удален из базы данных
2. Все связанные данные аккаунта удалены или помечены как удаленные
3. Поле accountId в ответе соответствует ID удаленного аккаунта
4. Последующие запросы к аккаунту возвращают 404
#### 3.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 3.3.3 Проверка состояния аккаунта
**Сценарий 3.1: Удаление уже удаленного аккаунта**
- Предусловие: Аккаунт уже помечен как удаленный
- Ожидаемый результат: 200 OK
- Проверка: повторное удаление не вызывает ошибок
**Сценарий 3.2: Удаление несуществующего аккаунта**
- Предусловие: Аккаунт не существует
- Ожидаемый результат: 200 OK
- Проверка: операция завершается успешно
#### 3.3.4 Проверка каскадного удаления
**Сценарий 4.1: Удаление связанных данных**
- Проверка: все связанные квизы удалены или помечены как удаленные
- Проверка: все связанные вопросы удалены или помечены как удаленные
- Проверка: все связанные результаты удалены или помечены как удаленные
**Сценарий 4.2: Сохранение статистики**
- Проверка: статистические данные сохранены для аналитики
- Проверка: исторические данные доступны для отчетов
#### 3.3.5 Проверка безопасности
**Сценарий 5.1: Попытка удаления чужого аккаунта**
- Предусловие: Попытка удаления аккаунта другого пользователя
- Ожидаемый результат: 401 Unauthorized
- Проверка: операция отклонена
**Сценарий 5.2: Проверка CSRF защиты**
- Проверка: наличие и валидность CSRF токена
- Проверка: отклонение запросов без валидного CSRF токена
#### 3.3.6 Проверка производительности
**Сценарий 6.1: Время удаления**
- Проверка: время удаления аккаунта < 2s
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы на удаление
- Проверка: отсутствие утечек памяти при массовом удалении
#### 3.3.7 Граничные случаи
**Сценарий 7.1: Удаление аккаунта с большим количеством данных**
- Предусловие: Аккаунт имеет множество связанных данных
- Ожидаемый результат: 200 OK
- Проверка: все данные успешно удалены
**Сценарий 7.2: Удаление во время активных операций**
- Предусловие: Активные операции с аккаунтом
- Ожидаемый результат: 200 OK
- Проверка: корректная обработка конкурентных запросов
#### 3.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 3.4 Особые моменты для тестирования
1. **Транзакционность:**
- Проверка атомарности операции удаления
- Проверка отката при частичном удалении
- Проверка целостности данных после удаления
2. **Логирование:**
- Проверка записи операции удаления
- Проверка аудит-логов
- Проверка логов связанных операций
3. **Мониторинг:**
- Проверка метрик удаления аккаунтов
- Проверка алертов при ошибках
- Проверка мониторинга связанных операций
4. **Восстановление:**
- Проверка возможности восстановления данных
- Проверка периода хранения удаленных данных
### 3.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время удаления аккаунта соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Каскадное удаление работает корректно
7. Данные успешно удаляются или помечаются как удаленные
## 4. GET /accounts
### 4.1 Описание
Получение списка аккаунтов с поддержкой пагинации.
### 4.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Поддерживает пагинацию через параметры limit и page
- Поддерживает HTTP коды ответа: 200, 400, 401, 500
### 4.3 Тестовые сценарии
#### 4.3.1 Успешное получение списка аккаунтов
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- В системе есть несколько аккаунтов
**Входные данные:**
- Метод: GET
- URL: /accounts
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"limit": 10,
"page": 1
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект со следующими полями:
- count (integer) - общее количество аккаунтов
- items (array) - массив объектов Account
**Проверки:**
1. Количество возвращаемых элементов не превышает limit
2. Все обязательные поля присутствуют в каждом объекте Account
3. Значение count соответствует общему количеству аккаунтов
4. Элементы отсортированы по дате создания (новые первые)
#### 4.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 4.3.3 Проверка пагинации
**Сценарий 3.1: Корректные параметры пагинации**
- Входные данные: limit=10, page=1
- Ожидаемый результат: 200 OK
- Проверка: возвращается корректное количество элементов
**Сценарий 3.2: Некорректные параметры пагинации**
- Входные данные: limit=0, page=0
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Превышение максимального лимита**
- Входные данные: limit=1000
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
#### 4.3.4 Проверка фильтрации
**Сценарий 4.1: Пустой результат**
- Предусловие: Нет аккаунтов в системе
- Ожидаемый результат: 200 OK
- Проверка: count=0, items=[]
**Сценарий 4.2: Последняя страница**
- Входные данные: page с большим номером
- Ожидаемый результат: 200 OK
- Проверка: items=[]
#### 4.3.5 Проверка безопасности
**Сценарий 5.1: Проверка прав доступа**
- Проверка: пользователь видит только разрешенные аккаунты
- Проверка: конфиденциальные данные скрыты
**Сценарий 5.2: CSRF защита**
- Проверка: наличие и валидность CSRF токена
- Проверка: отклонение запросов без валидного CSRF токена
#### 4.3.6 Проверка производительности
**Сценарий 6.1: Время ответа**
- Проверка: время ответа < 500ms
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система выдерживает 100 последовательных запросов
- Проверка: отсутствие утечек памяти
#### 4.3.7 Граничные случаи
**Сценарий 7.1: Большое количество аккаунтов**
- Предусловие: В системе 1000+ аккаунтов
- Ожидаемый результат: 200 OK
- Проверка: корректная работа пагинации
**Сценарий 7.2: Специальные символы в данных**
- Проверка: корректная обработка Unicode символов
- Проверка: корректное отображение специальных символов
#### 4.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 4.4 Особые моменты для тестирования
1. **Кэширование:**
- Проверка правильности кэширования результатов
- Проверка инвалидации кэша при изменении данных
2. **Логирование:**
- Проверка записи запросов к списку аккаунтов
- Проверка аудит-логов
3. **Мониторинг:**
- Проверка метрик производительности
- Проверка алертов при ошибках
4. **Оптимизация:**
- Проверка эффективности запросов к БД
- Проверка использования индексов
### 4.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время ответа соответствует требованиям
3. Пагинация работает корректно
4. Обработка ошибок соответствует спецификации
5. Безопасность соответствует стандартам
6. Логирование и мониторинг работают корректно
7. Кэширование работает эффективно
## 5. GET /privilege/{userId}
### 5.1 Описание
Получение списка привилегий для указанного пользователя по его ID.
### 5.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Требуется передача userId в теле запроса
- Поддерживает HTTP коды ответа: 200, 400, 401, 500
### 5.3 Тестовые сценарии
#### 5.3.1 Успешное получение привилегий
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- Указанный пользователь существует
- У пользователя есть привилегии
**Входные данные:**
- Метод: GET
- URL: /privilege/{userId}
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"userId": "valid_user_id"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит массив объектов ShortPrivilege со следующими полями:
- id (string)
- privilege_id (string)
- privilege_name (string)
- amount (integer)
- created_at (date-time)
**Проверки:**
1. Все привилегии пользователя возвращены
2. Все обязательные поля присутствуют в каждом объекте
3. Формат даты created_at соответствует ISO 8601
4. Значения полей соответствуют данным в базе
#### 5.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 5.3.3 Проверка входных данных
**Сценарий 3.1: Отсутствие userId**
- Body: пустой JSON объект
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Невалидный userId**
- Body: { "userId": "invalid_id" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Несуществующий userId**
- Body: { "userId": "non_existent_id" }
- Ожидаемый результат: 200 OK
- Проверка: возвращается пустой массив
#### 5.3.4 Проверка прав доступа
**Сценарий 4.1: Запрос привилегий другого пользователя**
- Предусловие: Запрос привилегий пользователя с другим ID
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
**Сценарий 4.2: Запрос привилегий с недостаточными правами**
- Предусловие: У запрашивающего пользователя нет прав на просмотр привилегий
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
#### 5.3.5 Проверка безопасности
**Сценарий 5.1: SQL-инъекции**
- Body: { "userId": "1' OR '1'='1" }
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 5.2: XSS-атаки**
- Body: { "userId": "" }
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 5.3.6 Проверка производительности
**Сценарий 6.1: Время ответа**
- Проверка: время ответа < 300ms
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система выдерживает 100 последовательных запросов
- Проверка: отсутствие утечек памяти
#### 5.3.7 Граничные случаи
**Сценарий 7.1: Пользователь с большим количеством привилегий**
- Предусловие: У пользователя 100+ привилегий
- Ожидаемый результат: 200 OK
- Проверка: все привилегии возвращены корректно
**Сценарий 7.2: Пользователь без привилегий**
- Предусловие: У пользователя нет привилегий
- Ожидаемый результат: 200 OK
- Проверка: возвращается пустой массив
#### 5.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 5.4 Особые моменты для тестирования
1. **Кэширование:**
- Проверка кэширования результатов запроса
- Проверка инвалидации кэша при изменении привилегий
2. **Логирование:**
- Проверка записи запросов к привилегиям
- Проверка аудит-логов доступа
3. **Мониторинг:**
- Проверка метрик производительности
- Проверка алертов при ошибках
4. **Безопасность:**
- Проверка контроля доступа
- Проверка валидации входных данных
### 5.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время ответа соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Кэширование работает эффективно
7. Контроль доступа работает корректно
## 6. DELETE /account/{userId}
### 6.1 Описание
Удаление аккаунта пользователя по его ID.
### 6.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Требуется передача userId в теле запроса
- Поддерживает HTTP коды ответа: 200, 400, 401, 500
### 6.3 Тестовые сценарии
#### 6.3.1 Успешное удаление аккаунта
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У запрашивающего пользователя есть права на удаление аккаунтов
- Удаляемый аккаунт существует и активен
**Входные данные:**
- Метод: DELETE
- URL: /account/{userId}
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"userId": "valid_user_id"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект с полем userId (string)
**Проверки:**
1. Аккаунт успешно удален из базы данных
2. Все связанные данные аккаунта удалены или помечены как удаленные
3. Поле userId в ответе соответствует ID удаленного аккаунта
4. Последующие запросы к аккаунту возвращают 404
#### 6.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 6.3.3 Проверка входных данных
**Сценарий 3.1: Отсутствие userId**
- Body: пустой JSON объект
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Невалидный userId**
- Body: { "userId": "invalid_id" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Несуществующий userId**
- Body: { "userId": "non_existent_id" }
- Ожидаемый результат: 200 OK
- Проверка: операция завершается успешно
#### 6.3.4 Проверка прав доступа
**Сценарий 4.1: Удаление без необходимых прав**
- Предусловие: У пользователя нет прав на удаление аккаунтов
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
**Сценарий 4.2: Попытка удаления системного аккаунта**
- Предусловие: Попытка удаления системного аккаунта
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
#### 6.3.5 Проверка каскадного удаления
**Сценарий 5.1: Удаление связанных данных**
- Проверка: все связанные квизы удалены или помечены как удаленные
- Проверка: все связанные вопросы удалены или помечены как удаленные
- Проверка: все связанные результаты удалены или помечены как удаленные
**Сценарий 5.2: Сохранение статистики**
- Проверка: статистические данные сохранены для аналитики
- Проверка: исторические данные доступны для отчетов
#### 6.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- Body: { "userId": "1' OR '1'='1" }
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 6.2: XSS-атаки**
- Body: { "userId": "" }
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 6.3.7 Проверка производительности
**Сценарий 7.1: Время удаления**
- Проверка: время удаления аккаунта < 2s
- Проверка: стабильность времени при повторных запросах
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы на удаление
- Проверка: отсутствие утечек памяти при массовом удалении
#### 6.3.8 Граничные случаи
**Сценарий 8.1: Удаление аккаунта с большим количеством данных**
- Предусловие: Аккаунт имеет множество связанных данных
- Ожидаемый результат: 200 OK
- Проверка: все данные успешно удалены
**Сценарий 8.2: Удаление во время активных операций**
- Предусловие: Активные операции с аккаунтом
- Ожидаемый результат: 200 OK
- Проверка: корректная обработка конкурентных запросов
#### 6.3.9 Обработка ошибок
**Сценарий 9.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 9.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 6.4 Особые моменты для тестирования
1. **Транзакционность:**
- Проверка атомарности операции удаления
- Проверка отката при частичном удалении
- Проверка целостности данных после удаления
2. **Логирование:**
- Проверка записи операции удаления
- Проверка аудит-логов
- Проверка логов связанных операций
3. **Мониторинг:**
- Проверка метрик удаления аккаунтов
- Проверка алертов при ошибках
- Проверка мониторинга связанных операций
4. **Восстановление:**
- Проверка возможности восстановления данных
- Проверка периода хранения удаленных данных
### 6.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время удаления аккаунта соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Каскадное удаление работает корректно
7. Данные успешно удаляются или помечаются как удаленные
8. Контроль доступа работает корректно
## 7. POST /account/manualdone
### 7.1 Описание
Ручная пометка аккаунта как завершенного.
### 7.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Требуется передача id пользователя в теле запроса
- Поддерживает HTTP коды ответа: 200, 400, 401, 404, 500
### 7.3 Тестовые сценарии
#### 7.3.1 Успешная пометка аккаунта как завершенного
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У запрашивающего пользователя есть права на пометку аккаунтов
- Аккаунт существует и активен
**Входные данные:**
- Метод: POST
- URL: /account/manualdone
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"id": "valid_user_id"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
**Проверки:**
1. Аккаунт успешно помечен как завершенный
2. Статус аккаунта обновлен в базе данных
3. Все связанные операции завершены
4. Последующие запросы к аккаунту показывают обновленный статус
#### 7.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 7.3.3 Проверка входных данных
**Сценарий 3.1: Отсутствие id**
- Body: пустой JSON объект
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Невалидный id**
- Body: { "id": "invalid_id" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Несуществующий id**
- Body: { "id": "non_existent_id" }
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
#### 7.3.4 Проверка прав доступа
**Сценарий 4.1: Отметка без необходимых прав**
- Предусловие: У пользователя нет прав на пометку аккаунтов
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
**Сценарий 4.2: Попытка пометки системного аккаунта**
- Предусловие: Попытка пометки системного аккаунта
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
#### 7.3.5 Проверка состояния аккаунта
**Сценарий 5.1: Пометка уже завершенного аккаунта**
- Предусловие: Аккаунт уже помечен как завершенный
- Ожидаемый результат: 200 OK
- Проверка: повторная пометка не вызывает ошибок
**Сценарий 5.2: Пометка удаленного аккаунта**
- Предусловие: Аккаунт помечен как удаленный
- Ожидаемый результат: 404 Not Found
- Проверка: операция отклонена
#### 7.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- Body: { "id": "1' OR '1'='1" }
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 6.2: XSS-атаки**
- Body: { "id": "" }
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 7.3.7 Проверка производительности
**Сценарий 7.1: Время выполнения**
- Проверка: время выполнения операции < 1s
- Проверка: стабильность времени при повторных запросах
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы
- Проверка: отсутствие утечек памяти
#### 7.3.8 Граничные случаи
**Сценарий 8.1: Пометка аккаунта с большим количеством данных**
- Предусловие: Аккаунт имеет множество связанных данных
- Ожидаемый результат: 200 OK
- Проверка: все данные успешно обработаны
**Сценарий 8.2: Пометка во время активных операций**
- Предусловие: Активные операции с аккаунтом
- Ожидаемый результат: 200 OK
- Проверка: корректная обработка конкурентных запросов
#### 7.3.9 Обработка ошибок
**Сценарий 9.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 9.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 7.4 Особые моменты для тестирования
1. **Транзакционность:**
- Проверка атомарности операции пометки
- Проверка отката при частичном выполнении
- Проверка целостности данных после пометки
2. **Логирование:**
- Проверка записи операции пометки
- Проверка аудит-логов
- Проверка логов связанных операций
3. **Мониторинг:**
- Проверка метрик выполнения операций
- Проверка алертов при ошибках
- Проверка мониторинга связанных операций
4. **Восстановление:**
- Проверка возможности отмены пометки
- Проверка истории изменений статуса
### 7.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время выполнения операции соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Транзакционность работает корректно
7. Статус аккаунта успешно обновляется
8. Контроль доступа работает корректно
## 8. POST /account/leadtarget
### 8.1 Описание
Добавление целевого адреса для отправки клиентских запросов.
### 8.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Обязательные поля в теле запроса: type, quizID, target
- Поддерживает HTTP коды ответа: 200, 208, 400, 401, 404, 500
### 8.3 Тестовые сценарии
#### 8.3.1 Успешное добавление целевого адреса
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У пользователя есть права на добавление целевых адресов
**Входные данные:**
- Метод: POST
- URL: /account/leadtarget
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"type": "mail",
"quizID": 123,
"target": "example@mail.com",
"name": "Example Channel"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
**Проверки:**
1. Целевой адрес успешно добавлен
2. Данные корректно сохранены в базе
3. Все обязательные поля присутствуют
4. Значения полей соответствуют переданным данным
#### 8.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 8.3.3 Проверка входных данных
**Сценарий 3.1: Отсутствие обязательных полей**
- Body: { "type": "mail" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Невалидный тип**
- Body: { "type": "invalid", "quizID": 123, "target": "example@mail.com" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Невалидный формат target**
- Body: { "type": "mail", "quizID": 123, "target": "invalid_email" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
#### 8.3.4 Проверка дублирования
**Сценарий 4.1: Добавление существующего target**
- Предусловие: Target уже существует
- Ожидаемый результат: 208 Already Reported
- Проверка сообщения об ошибке
**Сценарий 4.2: Добавление с разным регистром**
- Предусловие: Target существует с другим регистром
- Ожидаемый результат: 208 Already Reported
- Проверка сообщения об ошибке
#### 8.3.5 Проверка безопасности
**Сценарий 5.1: SQL-инъекции**
- Body: { "type": "mail", "quizID": "1' OR '1'='1", "target": "example@mail.com" }
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 5.2: XSS-атаки**
- Body: { "type": "mail", "quizID": 123, "target": "" }
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 8.3.6 Проверка производительности
**Сценарий 6.1: Время выполнения**
- Проверка: время выполнения операции < 1s
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы
- Проверка: отсутствие утечек памяти
#### 8.3.7 Граничные случаи
**Сценарий 7.1: Максимальная длина полей**
- Body: { "type": "mail", "quizID": 123, "target": "very_long_email@domain.com", "name": "very_long_name" }
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
**Сценарий 7.2: Специальные символы**
- Body: { "type": "mail", "quizID": 123, "target": "special!@#$%^&*()@domain.com", "name": "Special Name!" }
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
#### 8.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 8.4 Особые моменты для тестирования
1. **Валидация данных:**
- Проверка форматов email, телефона, ID канала
- Проверка уникальности target
- Проверка допустимых значений type
2. **Транзакционность:**
- Проверка атомарности операции добавления
- Проверка отката при ошибках
3. **Логирование:**
- Проверка записи операции добавления
- Проверка аудит-логов
4. **Мониторинг:**
- Проверка метрик добавления target
- Проверка алертов при ошибках
### 8.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время выполнения операции соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Валидация данных работает корректно
7. Дублирование target обрабатывается корректно
## 9. PUT /account/leadtarget
### 9.1 Описание
Обновление существующего целевого адреса.
### 9.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- Обязательные поля в теле запроса: id, target
- Поддерживает HTTP коды ответа: 200, 400, 401, 404, 500
### 9.3 Тестовые сценарии
#### 9.3.1 Успешное обновление целевого адреса
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У пользователя есть права на обновление целевых адресов
- Целевой адрес существует
**Входные данные:**
- Метод: PUT
- URL: /account/leadtarget
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Body:
```json
{
"id": 123,
"target": "new_target@mail.com"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
**Проверки:**
1. Целевой адрес успешно обновлен
2. Данные корректно сохранены в базе
3. Старое значение target заменено на новое
4. Остальные поля остались без изменений
#### 9.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 9.3.3 Проверка входных данных
**Сценарий 3.1: Отсутствие обязательных полей**
- Body: { "id": 123 }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Невалидный id**
- Body: { "id": "invalid", "target": "example@mail.com" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Невалидный формат target**
- Body: { "id": 123, "target": "invalid_email" }
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
#### 9.3.4 Проверка существования
**Сценарий 4.1: Обновление несуществующего target**
- Body: { "id": 999999, "target": "example@mail.com" }
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
**Сценарий 4.2: Обновление удаленного target**
- Предусловие: Target помечен как удаленный
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
#### 9.3.5 Проверка безопасности
**Сценарий 5.1: SQL-инъекции**
- Body: { "id": "1' OR '1'='1", "target": "example@mail.com" }
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 5.2: XSS-атаки**
- Body: { "id": 123, "target": "" }
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 9.3.6 Проверка производительности
**Сценарий 6.1: Время выполнения**
- Проверка: время выполнения операции < 1s
- Проверка: стабильность времени при повторных запросах
**Сценарий 6.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы
- Проверка: отсутствие утечек памяти
#### 9.3.7 Граничные случаи
**Сценарий 7.1: Максимальная длина target**
- Body: { "id": 123, "target": "very_long_email@very_long_domain.com" }
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
**Сценарий 7.2: Специальные символы**
- Body: { "id": 123, "target": "special!@#$%^&*()@domain.com" }
- Ожидаемый результат: 200 OK
- Проверка: данные сохранены корректно
#### 9.3.8 Обработка ошибок
**Сценарий 8.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 8.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 9.4 Особые моменты для тестирования
1. **Валидация данных:**
- Проверка форматов email, телефона, ID канала
- Проверка уникальности нового target
- Проверка существования id
2. **Транзакционность:**
- Проверка атомарности операции обновления
- Проверка отката при ошибках
3. **Логирование:**
- Проверка записи операции обновления
- Проверка аудит-логов
- Проверка истории изменений
4. **Мониторинг:**
- Проверка метрик обновления target
- Проверка алертов при ошибках
### 9.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время выполнения операции соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Валидация данных работает корректно
7. Обновление target выполняется корректно
8. История изменений сохраняется
## 10. DELETE /account/leadtarget/{id}
### 10.1 Описание
Удаление целевого адреса по его ID.
### 10.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- ID целевого адреса передается в пути URL
- Поддерживает HTTP коды ответа: 200, 400, 401, 500
### 10.3 Тестовые сценарии
#### 10.3.1 Успешное удаление целевого адреса
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У пользователя есть права на удаление целевых адресов
- Целевой адрес существует и активен
**Входные данные:**
- Метод: DELETE
- URL: /account/leadtarget/{valid_id}
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
**Проверки:**
1. Целевой адрес успешно удален
2. Данные корректно удалены из базы
3. Последующие запросы к этому ID возвращают 404
4. Все связанные данные обработаны корректно
#### 10.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 10.3.3 Проверка входных данных
**Сценарий 3.1: Невалидный ID**
- URL: /account/leadtarget/invalid_id
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Отсутствующий ID**
- URL: /account/leadtarget/
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Несуществующий ID**
- URL: /account/leadtarget/999999
- Ожидаемый результат: 200 OK
- Проверка: операция завершается успешно
#### 10.3.4 Проверка прав доступа
**Сценарий 4.1: Удаление без необходимых прав**
- Предусловие: У пользователя нет прав на удаление целевых адресов
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
**Сценарий 4.2: Попытка удаления чужого целевого адреса**
- Предусловие: Целевой адрес принадлежит другому пользователю
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
#### 10.3.5 Проверка состояния целевого адреса
**Сценарий 5.1: Удаление уже удаленного целевого адреса**
- Предусловие: Целевой адрес уже помечен как удаленный
- Ожидаемый результат: 200 OK
- Проверка: повторное удаление не вызывает ошибок
**Сценарий 5.2: Удаление активного целевого адреса**
- Предусловие: Целевой адрес активен и используется
- Ожидаемый результат: 200 OK
- Проверка: все связанные операции обработаны корректно
#### 10.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- URL: /account/leadtarget/1' OR '1'='1
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 6.2: XSS-атаки**
- URL: /account/leadtarget/
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 10.3.7 Проверка производительности
**Сценарий 7.1: Время выполнения**
- Проверка: время выполнения операции < 1s
- Проверка: стабильность времени при повторных запросах
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: система корректно обрабатывает множественные запросы на удаление
- Проверка: отсутствие утечек памяти
#### 10.3.8 Граничные случаи
**Сценарий 8.1: Удаление целевого адреса с большим количеством связанных данных**
- Предусловие: Целевой адрес имеет множество связанных данных
- Ожидаемый результат: 200 OK
- Проверка: все данные успешно удалены
**Сценарий 8.2: Удаление во время активных операций**
- Предусловие: Активные операции с целевым адресом
- Ожидаемый результат: 200 OK
- Проверка: корректная обработка конкурентных запросов
#### 10.3.9 Обработка ошибок
**Сценарий 9.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 9.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 10.4 Особые моменты для тестирования
1. **Транзакционность:**
- Проверка атомарности операции удаления
- Проверка отката при частичном удалении
- Проверка целостности данных после удаления
2. **Логирование:**
- Проверка записи операции удаления
- Проверка аудит-логов
- Проверка логов связанных операций
3. **Мониторинг:**
- Проверка метрик удаления целевых адресов
- Проверка алертов при ошибках
- Проверка мониторинга связанных операций
4. **Восстановление:**
- Проверка возможности восстановления данных
- Проверка периода хранения удаленных данных
### 10.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время выполнения операции соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Транзакционность работает корректно
7. Целевой адрес успешно удаляется
8. Контроль доступа работает корректно
## 11. GET /account/leadtarget/{quizID}
### 11.1 Описание
Получение целевого адреса по ID квиза.
### 11.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth)
- ID квиза передается в пути URL
- Поддерживает HTTP коды ответа: 200, 400, 401, 404, 500
### 11.3 Тестовые сценарии
#### 11.3.1 Успешное получение целевого адреса
**Предусловия:**
- Пользователь авторизован
- JWT токен валиден
- У пользователя есть права на просмотр целевых адресов
- Квиз существует и имеет связанный целевой адрес
**Входные данные:**
- Метод: GET
- URL: /account/leadtarget/{valid_quiz_id}
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект LeadTarget со следующими полями:
- id (integer)
- accountID (string)
- type (string)
- target (string)
- quizID (integer)
- inviteLink (string, опционально)
- deleted (boolean)
- createdAt (date-time)
**Проверки:**
1. Все обязательные поля присутствуют в ответе
2. Формат даты createdAt соответствует ISO 8601
3. Значения полей соответствуют данным в базе
4. Тип целевого адреса соответствует ожидаемому
#### 11.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_token
- Ожидаемый результат: 401 Unauthorized
- Проверка сообщения об ошибке
#### 11.3.3 Проверка входных данных
**Сценарий 3.1: Невалидный quizID**
- URL: /account/leadtarget/invalid_id
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.2: Отсутствующий quizID**
- URL: /account/leadtarget/
- Ожидаемый результат: 400 Bad Request
- Проверка сообщения об ошибке
**Сценарий 3.3: Несуществующий quizID**
- URL: /account/leadtarget/999999
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
#### 11.3.4 Проверка прав доступа
**Сценарий 4.1: Просмотр без необходимых прав**
- Предусловие: У пользователя нет прав на просмотр целевых адресов
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
**Сценарий 4.2: Попытка просмотра чужого целевого адреса**
- Предусловие: Целевой адрес принадлежит другому пользователю
- Ожидаемый результат: 401 Unauthorized
- Проверка: доступ запрещен
#### 11.3.5 Проверка состояния целевого адреса
**Сценарий 5.1: Получение удаленного целевого адреса**
- Предусловие: Целевой адрес помечен как удаленный
- Ожидаемый результат: 404 Not Found
- Проверка сообщения об ошибке
**Сценарий 5.2: Получение активного целевого адреса**
- Предусловие: Целевой адрес активен
- Ожидаемый результат: 200 OK
- Проверка: все данные возвращены корректно
#### 11.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- URL: /account/leadtarget/1' OR '1'='1
- Ожидаемый результат: 400 Bad Request
- Проверка: инъекция не выполнена
**Сценарий 6.2: XSS-атаки**
- URL: /account/leadtarget/
- Ожидаемый результат: 400 Bad Request
- Проверка: XSS-код не выполнен
#### 11.3.7 Проверка производительности
**Сценарий 7.1: Время выполнения**
- Проверка: время выполнения операции < 300ms
- Проверка: стабильность времени при повторных запросах
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: система выдерживает 100 последовательных запросов
- Проверка: отсутствие утечек памяти
#### 11.3.8 Граничные случаи
**Сценарий 8.1: Получение целевого адреса с большим количеством данных**
- Предусловие: Целевой адрес имеет множество связанных данных
- Ожидаемый результат: 200 OK
- Проверка: все данные возвращены корректно
**Сценарий 8.2: Получение во время активных операций**
- Предусловие: Активные операции с целевым адресом
- Ожидаемый результат: 200 OK
- Проверка: корректная обработка конкурентных запросов
#### 11.3.9 Обработка ошибок
**Сценарий 9.1: Внутренняя ошибка сервера**
- Предусловие: Имитация сбоя базы данных
- Ожидаемый результат: 500 Internal Server Error
- Проверка: информативное сообщение об ошибке
**Сценарий 9.2: Таймаут соединения**
- Предусловие: Имитация таймаута
- Ожидаемый результат: 500 Internal Server Error
- Проверка: корректная обработка таймаута
### 11.4 Особые моменты для тестирования
1. **Кэширование:**
- Проверка правильности кэширования результатов
- Проверка инвалидации кэша при изменении данных
- Проверка TTL кэша
2. **Логирование:**
- Проверка записи запросов к целевым адресам
- Проверка аудит-логов
- Проверка логов доступа
3. **Мониторинг:**
- Проверка метрик производительности
- Проверка алертов при ошибках
- Проверка мониторинга доступа
4. **Безопасность:**
- Проверка контроля доступа
- Проверка валидации входных данных
- Проверка защиты от атак
### 11.5 Критерии приемки
1. Все тестовые сценарии пройдены успешно
2. Время ответа соответствует требованиям
3. Обработка ошибок соответствует спецификации
4. Безопасность соответствует стандартам
5. Логирование и мониторинг работают корректно
6. Кэширование работает эффективно
7. Контроль доступа работает корректно
8. Данные возвращаются в корректном формате
## 12. POST /question/create
### 12.1 Описание
Создание нового вопроса в системе. Эндпоинт позволяет добавлять вопросы к существующим квизам или как независимые сущности, в зависимости от логики приложения и схемы `QuestionCreateRequest`.
### 12.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Создает новый вопрос в системе.
- Тело запроса должно соответствовать схеме `#/components/schemas/QuestionCreateRequest`.
- В случае успеха возвращает созданный вопрос согласно схеме `#/components/schemas/Question`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 422 (Unprocessable Entity), 424 (Failed Dependency), 500 (Internal Server Error).
### 12.3 Тестовые сценарии
#### 12.3.1 Успешное создание вопроса
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Данные для создания вопроса корректны и полны (например, указан существующий `quiz_id`).
**Входные данные:**
- Метод: POST
- URL: /question/create
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно схеме `#/components/schemas/QuestionCreateRequest`. Пример для типа "variant"):
```json
{
"quiz_id": 12345, // integer, обязательное поле
"title": "Какой основной компонент воздуха?", // string, обязательное поле (max 512 chars)
"type": "variant", // string, обязательное поле, enum: [text, variant, images, select, varimg, emoji, date, number, page, rating, result, file]
"description": "Выберите один правильный ответ.", // string, опционально
"required": true, // boolean, опционально
"page": 1, // integer, опционально
"content": "{}" // string, JSON serialized config, опционально. В данном примере - строка, содержащая пустой JSON объект.
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа содержит объект созданного вопроса (структура и поля зависят от схемы `#/components/schemas/Question`. Пример, который может потребовать уточнений на основе схемы `Question`):
```json
{
"id": "generated_question_id",
"quiz_id": 12345,
"title": "Какой основной компонент воздуха?",
"type": "variant",
"description": "Выберите один правильный ответ.",
"required": true,
"page": 1,
"content": "{}", // В данном примере - строка, содержащая пустой JSON объект.
"created_at": "YYYY-MM-DDTHH:mm:ssZ"
// ...другие поля в соответствии со схемой Question
}
```
**Проверки:**
1. Вопрос успешно создан в базе данных с переданными атрибутами.
2. Ответ содержит все обязательные поля согласно схеме `Question`.
3. `id` созданного вопроса уникален.
4. `quiz_id` в ответе соответствует переданному значению и имеет тип integer.
5. `title`, `type`, `description`, `required`, `page` в ответе соответствуют переданным значениям.
6. Поле `content` в ответе содержит строку с JSON-конфигурацией. В данном примере ожидается строка "{}".
7. Если вопрос привязан к квизу (`quiz_id`), эта связь корректно установлена.
8. Формат `created_at` (и других дат) соответствует ISO 8601.
#### 12.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации (согласно `#/components/responses/UnauthorizedError`).
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
#### 12.3.3 Проверка валидации данных (Ответ 400 Bad Request или 422 Unprocessable Entity)
**Сценарий 3.1: Отсутствие обязательных полей в `QuestionCreateRequest`**
- Входные данные: Тело запроса без одного или нескольких обязательных полей (например, без `title`, `quiz_id` или `type`).
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на отсутствующие или невалидные поля (для 422, тело ответа должно содержать `message` со деталями).
**Сценарий 3.2: Некорректный формат данных в полях `QuestionCreateRequest`**
- Входные данные: Тело запроса с полями неправильного типа (например, `quiz_id` не integer, `type` не из списка enum, `required` не boolean, `content` - невалидный JSON в строке).
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на поля с некорректным форматом.
**Сценарий 3.3: Некорректные значения полей (нарушение бизнес-логики)**
- Входные данные: Например, попытка создать вопрос для несуществующего `quiz_id`. `title` превышает 512 символов. Некорректная структура JSON в поле `content` для выбранного `type` вопроса.
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке описывает проблему валидации.
#### 12.3.4 Проверка ответа 406 Not Acceptable
**Сценарий 4.1: Условия, приводящие к 406 Not Acceptable**
- Предусловия: (Необходимо определить конкретные условия на основе спецификации `StatusNotAcceptableError`. Например, квиз, к которому добавляется вопрос, находится в статусе, не допускающем изменений, или превышен лимит вопросов).
- Входные данные: Корректный запрос на создание вопроса, но выполнены условия для ответа 406.
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusNotAcceptableError` и содержит информативное сообщение.
#### 12.3.5 Проверка ответа 424 Failed Dependency
**Сценарий 5.1: Имитация сбоя внешней зависимости**
- Предусловия: Имитация сбоя критической внешней службы или ресурса, от которого зависит создание вопроса (например, недоступность базы данных для проверки `quiz_id` или генерации ID).
- Входные данные: Корректный запрос на создание вопроса.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusFailedDependencyError` и содержит информативное сообщение.
#### 12.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- Входные данные: В строковых полях запроса передаются SQL-инъекции.
- Ожидаемый результат: 400 Bad Request / 422 Unprocessable Entity, либо запрос обработан без выполнения инъекции, данные сохранены корректно (экранированы).
- Проверка: Отсутствие уязвимости к SQL-инъекциям. Данные в БД не повреждены.
**Сценарий 6.2: XSS-атаки**
- Входные данные: В строковых полях запроса передаются XSS-скрипты.
- Ожидаемый результат: 400 Bad Request / 422 Unprocessable Entity, либо данные сохранены корректно (экранированы), и XSS не выполняется при отображении этих данных.
- Проверка: Отсутствие уязвимости к XSS.
#### 12.3.7 Проверка производительности
**Сценарий 7.1: Время создания вопроса**
- Проверка: Время ответа на запрос создания вопроса < N ms (например, < 500ms).
- Проверка: Стабильность времени ответа при многократных запросах.
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: Система способна обработать X запросов на создание вопросов в секунду/минуту без значительного ухудшения производительности или ошибок.
#### 12.3.8 Обработка ошибок (Общие)
**Сценарий 8.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация непредвиденного сбоя на сервере при обработке запроса (например, исключение в коде, не связанное с зависимостями или валидацией).
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа соответствует схеме `#/components/responses/InternalServerError` и содержит общее сообщение об ошибке, не раскрывая чувствительной информации.
### 12.4 Особые моменты для тестирования
1. **Валидация схемы `QuestionCreateRequest`:**
* Тщательная проверка всех обязательных (`quiz_id`, `title`, `type`) и опциональных (`description`, `required`, `page`, `content`) полей.
* Проверка типов данных: `quiz_id` (integer), `title` (string, max 512), `description` (string), `type` (string, enum: `[text, variant, images, select, varimg, emoji, date, number, page, rating, result, file]`), `required` (boolean), `page` (integer), `content` (string, должен быть валидным JSON; для примера достаточно "{}").
* Проверка граничных значений: длина `title`.
* Валидация JSON-структуры внутри строки `content` в зависимости от `type` вопроса (например, для `variant` или `select` JSON может содержать массив опций, для `rating` - параметры шкалы и т.д.; для общего случая или если структура не определена, строка "{}" является валидным значением).
* Проверка поведения при отсутствии опциональных полей.
2. **Логика типов вопросов:**
* Корректное создание и сохранение вопросов всех поддерживаемых типов.
* Проверка, что для каждого типа вопроса сохраняется и обрабатывается релевантная информация (например, `options` для типов с выбором ответа).
3. **Привязка к квизу (если применимо):**
* Успешное создание вопроса с корректным `quiz_id`.
* Поведение системы при указании несуществующего `quiz_id` (ожидается ошибка 400/422 или 404, в зависимости от реализации).
* Поведение при попытке создать вопрос без `quiz_id` (если это поле опционально).
4. **Конкурентное создание:**
* Попытка одновременного создания нескольких вопросов (особенно если есть генерация порядковых номеров или другие неатомарные операции).
5. **Интеграционные проверки:**
* Убедиться, что созданный вопрос корректно отображается при запросе квиза, к которому он был добавлен (если есть такая связь).
* Проверить, как вопрос влияет на другие части системы (например, подсчет вопросов в квизе).
### 12.5 Критерии приемки
1. Все тестовые сценарии из раздела 12.3 пройдены успешно.
2. Время ответа на создание вопроса соответствует установленным требованиям производительности.
3. Обработка всех кодов ошибок (200, 400, 401, 406, 422, 424, 500) соответствует спецификации API и предоставляет корректные сообщения.
4. Обеспечена защита от основных уязвимостей безопасности (SQL-инъекции, XSS).
5. Созданный вопрос корректно сохраняется в базе данных со всеми указанными атрибутами и доступен для последующего использования.
6. Логирование операций по созданию вопросов информативно и покрывает как успешные случаи, так и ошибки.
## 13. POST /question/getList
### 13.1 Описание
Получение списка вопросов с поддержкой пагинации и возможной фильтрацией. Эндпоинт используется для отображения вопросов в интерфейсах администрирования, привязки к квизам или других сценариях, где требуется доступ к списку вопросов.
### 13.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetQuestionListRequest` (обычно включает параметры пагинации `page`, `limit` и опциональные фильтры).
- В случае успеха возвращает список вопросов и метаданные пагинации согласно схеме `#/components/schemas/GetQuestionListResponse`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 500 (Internal Server Error).
### 13.3 Тестовые сценарии
#### 13.3.1 Успешное получение списка вопросов (с пагинацией)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- В системе существуют вопросы, соответствующие критериям запроса (если есть фильтры).
**Входные данные:**
- Метод: POST
- URL: /question/getList
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (пример, структура соответствует `#/components/schemas/GetQuestionListRequest`. Все поля опциональны, если не указано иное в схеме.):
```json
{
"limit": 10, // integer, формат uint64
"page": 1, // integer, формат uint64
"quiz_id": 123, // integer, формат uint64, ID квиза
"type": "variant", // string, enum: [text, variant, images, select, varimg, emoji, date, number, page, rating, result, file]
"from": 1678886400, // integer, int64, начало периода (timestamp)
"to": 1679145600, // integer, int4, конец периода (timestamp)
"search": "воздух", // string, строка для поиска
"deleted": false, // boolean, получить только удаленные (true) или только не удаленные (false/отсутствует)
"required": true // boolean, фильтр по обязательности вопроса
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
-- Тело ответа (пример, структура соответствует `#/components/schemas/GetQuestionListResponse` и `#/components/schemas/Question`):
```json
{
"count": 1, // integer, uint64 - Общее количество вопросов, соответствующих фильтрам
"items": [
{
"id": 101, // integer, uint64
"quiz_id": 123, // integer, uint64
"title": "Какой основной компонент воздуха?", // string
"description": "Выберите один правильный ответ из предложенных.", // string
"type": "variant", // string, enum
"required": true, // boolean
"deleted": false, // boolean
"page": 1, // integer
"content": "{\"options\":[{\"text\":\"Азот\"},{\"text\":\"Кислород\"}], \"answer\":\"Азот\"}", // string, Serialized JSON content
"version": 1, // integer
"parent_ids": [], // array of integers (int32)
"created_at": "2023-03-15T10:00:00Z", // string, date-time
"updated_at": "2023-03-15T11:30:00Z" // string, date-time
}
// ...могут быть другие вопросы, если limit > 1 и count > 1
]
}
```
**Проверки:**
1. Ответ содержит корректное количество вопросов в массиве `items`, не превышающее запрошенный `limit` (если `limit` был в запросе и меньше, чем `count`).
-2. Поля `total_count`, `page`, `limit` в ответе соответствуют запрошенным и реальным данным.
-3. Каждый объект вопроса в массиве `items` соответствует схеме `Question` и содержит все ожидаемые поля.
+2. Поле `count` в ответе содержит общее количество вопросов, соответствующих критериям фильтрации.
+3. Каждый объект вопроса в массиве `items` соответствует схеме `Question` и содержит все поля: `id`, `quiz_id`, `title`, `description`, `type`, `required`, `deleted`, `page`, `content`, `version`, `parent_ids`, `created_at`, `updated_at`, с корректными типами данных.
4. Если применялись фильтры (`quiz_id`, `type`, `from`/`to`, `search`, `deleted`, `required`), то все возвращенные вопросы соответствуют этим фильтрам (например, созданы в указанный период `from`-`to`, содержат `search` строку в релевантных полях, имеют соответствующий статус `deleted` и `required`).
#### 13.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации (согласно `#/components/responses/UnauthorizedError`).
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
#### 13.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
(Все поля в `GetQuestionListRequest` являются опциональными, если не указано иное в схеме. Проверки ниже касаются корректности форматов и значений, если поля предоставлены.)
**Сценарий 3.1: Некорректные параметры пагинации**
-- Входные данные: `page` или `limit` не являются integer, или имеют некорректный формат (например, отрицательные значения, если не разрешены).
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на невалидные параметры пагинации.
**Сценарий 3.2: Некорректный формат фильтров**
-- Входные данные:
+ - `quiz_id`, `from`, `to` не являются integer.
+ - `type` не из списка допустимых enum значений.
+ - `deleted` или `required` не являются boolean.
+ - `search` имеет недопустимый формат (если есть ограничения, например, по длине).
+ - `from` > `to` (некорректный временной диапазон).
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на невалидные параметры фильтрации.
-**Сценарий 3.3: Отсутствие обязательных полей в запросе (если есть)**
-- Входные данные: Тело запроса без обязательных полей (например, если `page` и `limit` обязательны).
-- Ожидаемый результат: 400 Bad Request.
-- Проверка: Сообщение об ошибке указывает на отсутствующие поля.
+**Сценарий 3.3: Невалидные значения для логических фильтров**
+- Входные данные: `deleted` или `required` переданы как строки (например, "true") вместо boolean `true`.
+ Ожидаемый результат: 400 Bad Request.
+ Проверка: Сообщение об ошибке указывает на невалидный тип данных для булевых фильтров.
#### 13.3.4 Проверка ответа 406 Not Acceptable
**Сценарий 4.1: Условия, приводящие к 406 Not Acceptable**
- Предусловия: (Необходимо определить конкретные условия на основе спецификации `StatusNotAcceptableError`. Например, запрос специфических данных, которые не могут быть предоставлены в текущем контексте).
- Входные данные: Корректный запрос на получение списка, но выполнены условия для ответа 406.
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusNotAcceptableError` и содержит информативное сообщение.
#### 13.3.5 Проверка ответа 424 Failed Dependency
**Сценарий 5.1: Имитация сбоя внешней зависимости**
- Предусловия: Имитация сбоя критической внешней службы или ресурса, от которого зависит создание вопроса (например, недоступность базы данных для проверки `quiz_id` или генерации ID).
- Входные данные: Корректный запрос на создание вопроса.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusFailedDependencyError` и содержит информативное сообщение.
#### 13.3.6 Обработка ошибок (Общие)
**Сценарий 6.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация непредвиденного сбоя на сервере (например, ошибка подключения к БД при выборке данных).
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа соответствует схеме `#/components/responses/InternalServerError`.
### 13.4 Особые моменты для тестирования
1. **Логика пагинации:**
* Корректность `total_count` при наличии/отсутствии фильтров.
* Правильность выдачи данных на первой, последней и промежуточных страницах.
* Поведение при запросе страницы, превышающей общее количество страниц (например, `items` - пустой массив, `page` равен запрошенному).
* Поведение при `limit=0` или очень большом `limit` (если есть ограничения).
2. **Логика фильтрации (согласно схеме `GetQuestionListRequest`):**
* Корректность работы каждого отдельного фильтра:
- `quiz_id`: выборка вопросов только для указанного квиза.
- `type`: выборка вопросов только указанного типа.
- `from`/`to`: выборка вопросов, созданных/измененных в указанном временном диапазоне. Проверить граничные условия.
- `search`: поиск по релевантным текстовым полям вопроса (например, `title`, `description`, текстовое содержимое в `content`). Проверить поиск по части слова, полному слову, нескольким словам. Чувствительность к регистру (если определено).
- `deleted`: корректная выборка только удаленных (`true`) или только неудаленных (`false` или отсутствие поля) вопросов.
- `required`: корректная выборка вопросов по флагу обязательности.
* Корректность работы комбинации нескольких фильтров одновременно (например, `quiz_id` + `type` + `search`).
* Поведение при передаче несуществующих значений для фильтров (например, `quiz_id`, которого нет; тип вопроса, который не используется).
* Поведение фильтров при наличии пустого значения (например, `search: ""`).
3. **Сортировка (если поддерживается):**
* Проверка сортировки по умолчанию (например, по дате создания).
* Проверка работы явной сортировки по различным полям и направлениям (ASC/DESC), если это предусмотрено в `GetQuestionListRequest`.
4. **Структура и полнота данных в ответе:**
* Каждый вопрос в списке `items` должен содержать все поля, определенные схемой `Question`.
* Проверка корректности данных для каждого поля вопроса (типы, форматы).
### 13.5 Критерии приемки
1. Все тестовые сценарии из раздела 13.3 пройдены успешно.
2. Время ответа на получение списка вопросов соответствует установленным требованиям производительности.
3. Пагинация и фильтрация (если реализованы) работают корректно и в соответствии с ожиданиями.
4. Обработка всех кодов ошибок (200, 400, 401, 406, 500) соответствует спецификации API и предоставляет корректные сообщения.
5. Данные в ответе полны, корректны и соответствуют схеме `GetQuestionListResponse` и `Question`.
6. Логирование запросов на получение списка вопросов информативно.
## 14. PATCH /question/edit
### 14.1 Описание
Обновление существующего вопроса. Позволяет изменять одно или несколько полей вопроса по его ID.
### 14.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: PATCH.
- Тело запроса должно соответствовать схеме `#/components/schemas/UpdateQuestionRequest` и содержать обязательное поле `id` вопроса.
- В случае успеха возвращает объект согласно схеме `#/components/schemas/UpdateQuestionResponse`, содержащий `updated` (ID обновленного вопроса).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 422 (Unprocessable Entity), 424 (Failed Dependency), 500 (Internal Server Error).
### 14.3 Тестовые сценарии
#### 14.3.1 Успешное обновление вопроса (изменение нескольких полей)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Вопрос с указанным `id` существует в системе.
**Входные данные:**
- Метод: PATCH
- URL: /question/edit
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/UpdateQuestionRequest`):
```json
{
"id": 101, // integer, uint64 - ID существующего вопроса
"title": "Обновленный заголовок вопроса?",
"desc": "Новое описание для вопроса.",
"type": "text",
"required": false,
"content": "{\"placeholder\":\"Введите ваш ответ здесь\"}",
"page": 2
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (согласно `#/components/schemas/UpdateQuestionResponse`):
```json
{
"updated": 101 // integer, uint64 - ID обновленного вопроса
}
```
**Проверки:**
1. Поле `updated` в ответе содержит `id` обновленного вопроса.
2. Данные вопроса в базе данных (или при последующем GET запросе вопроса) обновлены в соответствии с переданными в запросе полями (`title`, `desc`, `type`, `required`, `content`, `page`).
3. Поля, которые не были переданы в запросе на обновление, остались без изменений в базе данных.
4. Поле `updated_at` для вопроса в базе данных обновлено.
#### 14.3.2 Успешное обновление одного поля вопроса
**Предусловия:**
- Пользователь авторизован, JWT токен валиден.
- Вопрос с `id` = 102 существует.
**Входные данные:**
- Метод: PATCH
- URL: /question/edit
- Headers: Authorization: Bearer {valid_jwt_token}, Content-Type: application/json
- Тело запроса:
```json
{
"id": 102,
"title": "Только заголовок обновлен"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Тело ответа: `{"updated": 102}`
**Проверки:**
1. `updated` в ответе равен 102.
2. В базе данных для вопроса с `id`=102 обновлен только `title`, остальные поля (desc, type, required и т.д.) не изменились.
3. `updated_at` обновлен.
#### 14.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request или 422 Unprocessable Entity)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса без поля `id`.
```json
{
"title": "Запрос без ID"
}
```
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity (в зависимости от того, как сервер обрабатывает отсутствие `id` до валидации остальных полей).
- Проверка: Сообщение об ошибке указывает на отсутствие или невалидность поля `id` (для 422, тело ответа может содержать `message` с деталями).
**Сценарий 3.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{
"id": "not_an_integer",
"title": "Невалидный ID"
}
```
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 3.3: Попытка обновления несуществующего вопроса**
- Входные данные: `id` = 99999 (несуществующий).
```json
{
"id": 99999,
"title": "Несуществующий вопрос"
}
```
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity (так как 404 не заявлен, возможно, ошибка валидации ID).
- Проверка: Сообщение об ошибке указывает, что вопрос с таким `id` не найден или `id` невалиден.
**Сценарий 3.4: Некорректные значения для обновляемых полей**
- Входные данные (примеры, тестировать каждое поле отдельно):
- `title` длиннее 512 символов.
- `type` не из списка допустимых enum значений.
- `required` не boolean (например, строка "true").
- `content` не является валидной JSON-строкой.
- `page` не integer (например, строка "2").
- `desc` имеет недопустимый формат (если есть ограничения).
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на конкретное поле и характер ошибки валидации.
#### 14.3.4 Проверка авторизации
**Сценарий 4.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 4.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 4.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 14.3.5 Проверка специфических кодов ответа
**Сценарий 5.1: Условия для 406 Not Acceptable**
- Предусловия: (Определить условия на основе спецификации `StatusNotAcceptableError`. Например, попытка изменить вопрос, который заблокирован для редактирования).
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа согласно `#/components/responses/StatusNotAcceptableError`.
**Сценарий 5.2: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой внешней системы, от которой зависит обновление вопроса).
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 14.3.6 Проверка безопасности (SQL-инъекции, XSS)
**Сценарий 6.1: SQL-инъекции в строковых полях**
- Входные данные: В полях `title`, `desc`, `content` передаются SQL-инъекции.
- Ожидаемый результат: 400/422, либо запрос обработан без выполнения инъекции, данные корректно сохранены/обновлены (экранированы).
- Проверка: Отсутствие уязвимости.
**Сценарий 6.2: XSS-атаки в строковых полях**
- Входные данные: В полях `title`, `desc`, `content` передаются XSS-скрипты.
- Ожидаемый результат: 400/422, либо данные корректно сохранены/обновлены (экранированы), XSS не выполняется при отображении этих данных.
- Проверка: Отсутствие уязвимости.
#### 14.3.7 Обработка ошибок (Общие)
**Сценарий 7.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация сбоя на сервере при обновлении (например, ошибка БД).
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 14.4 Особые моменты для тестирования
1. **Частичное обновление**: Убедиться, что при обновлении только некоторых полей остальные поля вопроса не затрагиваются и сохраняют свои предыдущие значения.
2. **Валидация `id`**: Тщательная проверка того, что `id` существует и принадлежит текущему пользователю/аккаунту (если есть такая логика авторизации на уровне записи).
3. **Валидация полей `UpdateQuestionRequest`**:
* Проверка всех полей на соответствие типам и форматам (`id` uint64, `title` string<=512, `desc` string, `type` enum, `required` boolean, `content` JSON string, `page` integer).
* Корректная обработка пустых значений для опциональных полей (например, если передать `title: ""` или `desc: null` - как это должно обрабатываться).
4. **Конкурентное обновление**: Попытка одновременного обновления одного и того же вопроса разными запросами. Проверить на наличие race conditions, корректность блокировок (если используются), и итоговое состояние данных.
5. **Поле `updated_at`**: Проверить, что это поле автоматически обновляется при любом успешном изменении вопроса.
6. **Влияние на связанные сущности**: Если изменение вопроса (например, типа или содержимого) может влиять на квизы, в которых он используется, или на ответы пользователей, это необходимо проверить (может выходить за рамки юнит-теста этого эндпоинта).
### 14.5 Критерии приемки
1. Все тестовые сценарии из раздела 14.3 пройдены успешно.
2. Вопрос корректно обновляется в базе данных, затрагиваются только переданные поля.
3. Обработка всех кодов ошибок (200, 400, 401, 406, 422, 424, 500) соответствует спецификации API.
4. Обеспечена защита от основных уязвимостей безопасности.
5. Поле `updated_at` вопроса обновляется корректно.
6. Логирование операций по обновлению вопросов информативно.
## 15. POST /question/copy
### 15.1 Описание
Создание копии существующего вопроса и привязка этой копии к указанному квизу. Оригинальный вопрос остается без изменений.
### 15.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/CopyQuestionRequest` и содержать обязательные поля: `id` (ID оригинального вопроса) и `quiz_id` (ID целевого квиза для копии).
- В случае успеха (200 OK) возвращает полный объект нового, скопированного вопроса согласно схеме `#/components/schemas/Question`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 15.3 Тестовые сценарии
#### 15.3.1 Успешное копирование вопроса
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Существует вопрос с `id`, указанным в запросе (оригинальный вопрос).
- Существует квиз с `quiz_id`, указанным в запросе (целевой квиз).
**Входные данные:**
- Метод: POST
- URL: /question/copy
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/CopyQuestionRequest`):
```json
{
"id": 101, // integer, uint64 - ID существующего вопроса для копирования
"quiz_id": 202 // integer, uint64 - ID квиза, в который будет помещена копия
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (новый скопированный вопрос, согласно `#/components/schemas/Question`. Пример):
```json
{
"id": 303, // integer, uint64 - НОВЫЙ ID для скопированного вопроса
"quiz_id": 202, // integer, uint64 - ID целевого квиза из запроса
"title": "Копия: Какой основной компонент воздуха?", // string - title оригинального вопроса (может быть с префиксом)
"description": "Выберите один правильный ответ из предложенных.", // string - description оригинального вопроса
"type": "variant", // string, enum - type оригинального вопроса
"required": true, // boolean - required оригинального вопроса
"deleted": false, // boolean - всегда false для новой копии
"page": 1, // integer - page оригинального вопроса (или сброшено/пересчитано для нового квиза)
"content": "{}", // string - content оригинального вопроса
"version": 1, // integer - version оригинального вопроса (или сброшено до 1)
"parent_ids": [], // array of integers - parent_ids оригинального вопроса (или очищено)
"created_at": "2023-03-16T12:00:00Z", // string, date-time - НОВАЯ дата создания
"updated_at": "2023-03-16T12:00:00Z" // string, date-time - НОВАЯ дата обновления (равна created_at)
}
```
**Проверки:**
1. В базе данных создан новый вопрос.
2. Возвращенный объект вопроса имеет новый, уникальный `id` (не равен `id` оригинала).
3. `quiz_id` в возвращенном объекте соответствует `quiz_id`, указанному в запросе.
4. Поля `title`, `description`, `type`, `required`, `content`, `page`, `version` (и возможно `parent_ids`) скопированы из оригинального вопроса (с возможными модификациями, например, префикс к `title` или сброс `version`/`page`/`parent_ids` – уточнить ожидаемое поведение).
5. Поле `deleted` у новой копии установлено в `false`.
6. Поля `created_at` и `updated_at` у новой копии являются свежими временными метками.
7. Оригинальный вопрос (с `id` из запроса) остался без изменений в базе данных.
#### 15.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 15.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательных полей**
- Входные данные: Тело запроса без `id` или без `quiz_id`.
```json
// Пример без id
{ "quiz_id": 202 }
// Пример без quiz_id
{ "id": 101 }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствующее обязательное поле.
**Сценарий 3.2: Некорректный тип данных для `id` или `quiz_id`**
- Входные данные: `id` или `quiz_id` переданы как строка или другой невалидный тип.
```json
{ "id": "not_an_integer", "quiz_id": 202 }
{ "id": 101, "quiz_id": "not_an_integer" }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для соответствующего поля.
**Сценарий 3.3: Копирование несуществующего вопроса**
- Входные данные: `id` = 99999 (несуществующий оригинальный вопрос).
```json
{ "id": 99999, "quiz_id": 202 }
```
- Ожидаемый результат: 400 Bad Request (или 404, если бы он был заявлен; в данном случае OpenAPI указывает 400).
- Проверка: Сообщение об ошибке указывает, что оригинальный вопрос с таким `id` не найден.
**Сценарий 3.4: Копирование в несуществующий квиз**
- Входные данные: `quiz_id` = 88888 (несуществующий целевой квиз).
```json
{ "id": 101, "quiz_id": 88888 }
```
- Ожидаемый результат: 400 Bad Request (или 404, если бы он был заявлен).
- Проверка: Сообщение об ошибке указывает, что целевой квиз с таким `quiz_id` не найден.
#### 15.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой при записи в базу данных из-за внешних проблем, не связанных с валидацией входных данных).
- Входные данные: Корректный запрос на копирование.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 15.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере в процессе копирования.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 15.4 Особые моменты для тестирования
1. **Генерация нового ID**: Убедиться, что у скопированного вопроса всегда генерируется новый, уникальный `id`.
2. **Копирование данных**: Детально проверить, какие именно поля копируются из оригинала, а какие устанавливаются по умолчанию или пересчитываются для новой копии (например, `title`, `description`, `type`, `required`, `content`, `page`, `version`, `parent_ids`). Уточнить политику копирования этих полей.
3. **Поле `deleted`**: Убедиться, что у новой копии поле `deleted` всегда `false`.
4. **Временные метки**: `created_at` и `updated_at` для копии должны быть новыми.
5. **Иммутабельность оригинала**: Оригинальный вопрос не должен быть затронут операцией копирования.
6. **Копирование в тот же квиз**: Проверить поведение, если `quiz_id` в запросе совпадает с `quiz_id` оригинального вопроса. Должна ли создаваться копия в том же квизе? Будут ли конфликты имен (если есть уникальность `title` в рамках квиза)?
7. **Права доступа**: Если есть ограничения на доступ к оригинальному вопросу или к целевому квизу, проверить их соблюдение.
### 15.5 Критерии приемки
1. Все тестовые сценарии из раздела 15.3 пройдены успешно.
2. Новый вопрос корректно создается в базе данных как копия оригинала, со всеми необходимыми изменениями (новый ID, целевой `quiz_id`, свежие timestamp'ы).
3. Оригинальный вопрос остается неизменным.
4. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
5. Логирование операций по копированию вопросов информативно.
## 16. POST /question/history
### 16.1 Описание
Получение истории изменений для указанного вопроса. Каждая запись в истории представляет собой состояние (версию) вопроса на определенный момент времени.
### 16.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetQuestionHistoryRequest` и содержать обязательное поле `id` вопроса, а также опциональные поля для пагинации `l` (limit) и `p` (page).
- В случае успеха (200 OK) возвращает объект, содержащий поле `items`. Поле `items` является массивом, где каждый элемент - это объект `Question`, представляющий одну версию из истории вопроса.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 16.3 Тестовые сценарии
#### 16.3.1 Успешное получение истории вопроса (с пагинацией)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Вопрос с указанным `id` существует и имеет несколько записей в истории (несколько версий).
**Входные данные:**
- Метод: POST
- URL: /question/history
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/GetQuestionHistoryRequest`):
```json
{
"id": 101, // integer, uint64 - ID вопроса
"l": 10, // integer, uint64 - Limit (количество записей на странице)
"p": 1 // integer, uint64 - Page (номер страницы, например, 1-based)
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (объект с полем `items`, содержащим массив версий вопроса):
```json
{
"items": [
{
"id": 101, // integer, uint64 - ID оригинального вопроса
"quiz_id": 123,
"title": "Обновленный заголовок вопроса (Версия 2)",
"description": "Новое описание для вопроса (Версия 2).",
"type": "text",
"required": false,
"deleted": false, // или true, если версия была удалена, а потом восстановлена
"page": 2,
"content": "{\"placeholder\":\"Версия 2\"}",
"version": 2, // integer - номер версии
"parent_ids": [],
"created_at": "2023-03-15T11:30:00Z", // Время создания/фиксации этой версии
"updated_at": "2023-03-15T11:30:00Z" // Обычно совпадает с created_at для записи истории
},
{
"id": 101, // ID оригинального вопроса
"quiz_id": 123,
"title": "Исходный заголовок вопроса (Версия 1)",
"description": "Начальное описание (Версия 1).",
"type": "variant",
"required": true,
"deleted": false,
"page": 1,
"content": "{\"options\":[...]}",
"version": 1,
"parent_ids": [],
"created_at": "2023-03-15T10:00:00Z",
"updated_at": "2023-03-15T10:00:00Z"
}
// ... другие версии, если l > 2 ...
]
}
```
**Проверки:**
1. Массив `items` в ответе содержит записи истории для вопроса с запрошенным `id`.
2. Количество элементов в массиве `items` не превышает значение `l` (limit) из запроса.
3. Если `p` (page) и `l` (limit) используются, пагинация работает корректно, возвращая соответствующий срез истории.
4. Каждая запись в массиве `items` является полным объектом `Question`, представляющим состояние вопроса на момент этой версии.
5. Поле `version` в каждой записи истории корректно отражает номер версии.
6. Записи истории отсортированы в ожидаемом порядке (например, по убыванию `version` или `created_at`/`updated_at` этой записи истории).
7. Если у вопроса нет истории (например, он только что создан и не изменялся), массив `items` содержит одну запись, представляющую текущее состояние вопроса с `version: 1`.
8. Если запрос сделан с `l=0` или без `l` и `p`, проверить поведение по умолчанию (например, возврат всех версий или первой страницы с лимитом по умолчанию).
#### 16.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 16.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса без поля `id`.
```json
{ "l": 10, "p": 1 }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`, `l`, или `p`**
- Входные данные (примеры):
- `id` как строка: `{ "id": "not_an_integer", "l": 10, "p": 1 }`
- `l` как строка: `{ "id": 101, "l": "ten", "p": 1 }`
- `p` как строка: `{ "id": 101, "l": 10, "p": "one" }`
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для соответствующего поля.
**Сценарий 3.3: Некорректные значения для `l` или `p` (если есть ограничения)**
- Входные данные (примеры):
- `l` отрицательное: `{ "id": 101, "l": -5, "p": 1 }`
- `p` отрицательное или нулевое (если нумерация с 1): `{ "id": 101, "l": 10, "p": 0 }`
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на недопустимое значение для `l` или `p`.
**Сценарий 3.4: Запрос истории для несуществующего вопроса**
- Входные данные: `id` = 99999 (несуществующий вопрос).
```json
{ "id": 99999, "l": 10, "p": 1 }
```
- Ожидаемый результат: 400 Bad Request (или возможно пустой массив `items` в ответе 200 OK, если это допустимое поведение для несуществующего ID – уточнить).
- Проверка: Если 400, сообщение об ошибке указывает, что вопрос не найден. Если 200 OK, `items` должен быть пустым.
#### 16.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой при чтении из базы данных истории).
- Входные данные: Корректный запрос на получение истории.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 16.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 16.4 Особые моменты для тестирования
1. **Порядок версий**: Убедиться, что версии в истории отсортированы последовательно и предсказуемо (например, по убыванию номера `version` или по убыванию `created_at` записи истории).
2. **Полнота данных в версиях**: Каждая версия вопроса в `items` должна содержать все поля из схемы `Question` и отражать состояние вопроса на момент создания этой версии.
3. **Пагинация (`l` и `p`)**: Тщательно проверить работу пагинации: корректность количества возвращаемых элементов, переход по страницам, поведение на первой и последней странице, поведение при запросе страницы за пределами существующей истории.
4. **История для нового/неизмененного вопроса**: Если вопрос только создан и не редактировался, эндпоинт должен вернуть массив `items` с одной записью (текущее состояние, `version: 1`).
5. **Глубина/ограничения истории**: Если система накладывает ограничения на количество хранимых версий или глубину истории, это необходимо проверить.
6. **Согласованность данных**: Поля `id` и `quiz_id` в объектах истории должны соответствовать оригинальному вопросу. Поле `version` должно быть уникальным для каждой записи истории одного вопроса и увеличиваться (или изменяться предсказуемо).
### 16.5 Критерии приемки
1. Все тестовые сценарии из раздела 16.3 пройдены успешно.
2. История вопроса возвращается корректно, со всеми версиями и точными данными для каждой версии.
3. Параметры пагинации `l` и `p` работают правильно.
4. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
5. Данные в ответе структурированы правильно (объект с полем `items`, которое является массивом объектов `Question`).
6. Логирование запросов к истории вопросов информативно.
## 17. DELETE /question/delete
### 17.1 Описание
Удаление (или деактивация) существующего вопроса по его ID. В зависимости от реализации, это может быть физическое удаление или "мягкое" удаление (пометка вопроса как удаленного).
### 17.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: DELETE.
- Тело запроса должно соответствовать схеме `#/components/schemas/DeactivateQuestionRequest` и содержать обязательное поле `id` удаляемого вопроса.
- В случае успеха (200 OK) возвращает объект согласно схеме `#/components/schemas/DeactivateQuestionResponse`, содержащий `deactivated` (ID удаленного/деактивированного вопроса).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 17.3 Тестовые сценарии
#### 17.3.1 Успешное удаление (деактивация) вопроса
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Вопрос с указанным `id` существует в системе и не помечен как удаленный.
**Входные данные:**
- Метод: DELETE
- URL: /question/delete
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/DeactivateQuestionRequest`):
```json
{
"id": 101 // integer, uint64 - ID существующего вопроса
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (согласно `#/components/schemas/DeactivateQuestionResponse`):
```json
{
"deactivated": 101 // integer, uint64 - ID деактивированного вопроса
}
```
**Проверки:**
1. Поле `deactivated` в ответе содержит `id` удаленного/деактивированного вопроса.
2. Вопрос в базе данных помечен как удаленный (например, поле `deleted` у объекта Question установлено в `true`, обновлено поле `updated_at`).
3. При попытке получить этот вопрос через GET-запросы (например, `/question/get/{id}` или в списках `/question/getList` без специального флага для удаленных) он либо не возвращается, либо возвращается с отметкой об удалении.
4. Повторный запрос на удаление этого же вопроса обрабатывается корректно (см. сценарий идемпотентности).
#### 17.3.2 Проверка идемпотентности удаления
**Предусловия:**
- Пользователь авторизован, JWT токен валиден.
- Вопрос с `id` = 101 уже был удален/деактивирован.
**Входные данные:**
- Метод: DELETE
- URL: /question/delete
- Headers: Authorization: Bearer {valid_jwt_token}, Content-Type: application/json
- Тело запроса: `{"id": 101}`
**Ожидаемый результат:**
- HTTP Status: 200 OK (или другой код, если система явно сообщает о том, что ресурс уже удален, но 200 OK для идемпотентности предпочтителен).
- Тело ответа: `{"deactivated": 101}`.
**Проверки:**
1. Операция не вызывает ошибок.
2. Состояние вопроса в базе данных не изменяется (он остается удаленным).
#### 17.3.3 Проверка авторизации
**Сценарий 3.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 3.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 3.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 17.3.4 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 4.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса - пустой JSON `{}`.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 4.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{ "id": "not_an_integer" }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 4.3: Попытка удаления несуществующего вопроса**
- Входные данные: `id` = 99999 (несуществующий).
```json
{ "id": 99999 }
```
- Ожидаемый результат: 400 Bad Request (или 404, если бы он был заявлен; OpenAPI указывает 400 для этого эндпоинта, что может означать ошибку валидации ID или что ресурс не найден, но обрабатывается как Bad Request).
- Проверка: Сообщение об ошибке указывает, что вопрос с таким `id` не найден или `id` невалиден.
#### 17.3.5 Проверка специфических кодов ответа
**Сценарий 5.1: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой при обновлении статуса вопроса в базе данных из-за внешних проблем).
- Входные данные: Корректный запрос на удаление.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 17.3.6 Обработка ошибок (Общие)
**Сценарий 6.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере в процессе удаления/деактивации.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 17.4 Особые моменты для тестирования
1. **Механизм удаления**: Подтвердить, используется ли "мягкое" удаление (установка флага `deleted: true` и обновление `updated_at` у объекта `Question`) или физическое удаление из базы данных. Схемы `DeactivateQuestionRequest` и `DeactivateQuestionResponse` сильно намекают на мягкое удаление.
2. **Идемпотентность**: Повторное удаление уже удаленного вопроса не должно приводить к ошибке и не должно изменять состояние системы.
3. **Влияние на связанные сущности**: Проверить, как удаление/деактивация вопроса влияет на квизы, в которых он используется (например, удаляется ли он из квизов, или квизы перестают его отображать). Как это влияет на уже существующие ответы пользователей на этот вопрос?
4. **Права доступа**: Если существуют специфические права на удаление вопросов (например, только автор вопроса или администратор), их необходимо проверить.
5. **Восстановление вопроса**: Если используется "мягкое" удаление, проверить, существует ли механизм восстановления вопроса и как он работает (это может быть другой эндпоинт).
### 17.5 Критерии приемки
1. Все тестовые сценарии из раздела 17.3 пройдены успешно.
2. Вопрос корректно помечается как удаленный в базе данных (или удаляется физически, в зависимости от реализации).
3. Операция идемпотентна.
4. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
5. Логирование операций по удалению/деактивации вопросов информативно.
## 18. POST /quiz/create
### 18.1 Описание
Создание нового квиза в системе. Эндпоинт позволяет определить различные параметры квиза, включая его название, описание, настройки поведения и статус.
### 18.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Создает новый квиз в системе.
- Тело запроса должно соответствовать схеме `#/components/schemas/CreateQuizRequest`.
- В случае успеха возвращает созданный квиз согласно схеме `#/components/schemas/Quiz` со статусом 201 Created.
- Поддерживает HTTP коды ответа: 201 (Created), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 409 (Conflict), 422 (Unprocessable Entity), 500 (Internal Server Error).
### 18.3 Тестовые сценарии
#### 18.3.1 Успешное создание квиза
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Данные для создания квиза корректны и полны.
**Входные данные:**
- Метод: POST
- URL: /quiz/create
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно схеме `#/components/schemas/CreateQuizRequest`. Пример минимального набора):
```json
{
"name": "Новый квиз по истории",
"description": "Тест на знание ключевых дат.",
"status": "draft",
"config": "{\"rules\":[]}"
}
```
- Пример запроса со всеми опциональными полями:
```json
{
"name": "Полный квиз по географии",
"description": "Детальный тест на знание столиц и стран.",
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"config": "{\"showCorrectAnswers\": true}",
"status": "start",
"limit": 100,
"due_to": 1700000000,
"question_cnt": 10,
"time_of_passing": 3600,
"pausable": true,
"super": false,
"group_id": null
}
```
**Ожидаемый результат:**
- HTTP Status: 201 Created
- Content-Type: application/json
- Тело ответа содержит объект созданного квиза (согласно схеме `#/components/schemas/Quiz`):
```json
{
"id": 123, // integer, uint64 - Новый ID квиза
"qid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // string - UUID
"accountid": "user_account_id", // string - ID аккаунта создателя
"deleted": false,
"archived": false,
"fingerprinting": true, // или false, в зависимости от запроса
"repeatable": true, // или false
"note_prevented": false, // или true
"mail_notifications": true, // или false
"unique_answers": false, // или true
"name": "Полный квиз по географии",
"description": "Детальный тест на знание столиц и стран.",
"config": "{\"showCorrectAnswers\": true}",
"status": "start", // или другой статус из запроса
"limit": 100,
"due_to": 1700000000,
"time_of_passing": 3600,
"pausable": true,
"version": 1,
"version_comment": null, // или начальный комментарий
"parent_ids": [],
"created_at": "YYYY-MM-DDTHH:mm:ssZ", // Текущая дата и время
"updated_at": "YYYY-MM-DDTHH:mm:ssZ", // Текущая дата и время
"questions_count": 0, // Изначально 0, если question_cnt не создает их сразу
"session_count": 0,
"passed_count": 0,
"average_time": 0,
"super": false,
"group_id": null
}
```
**Проверки:**
1. Квиз успешно создан в базе данных с переданными атрибутами.
2. Ответ содержит все обязательные поля согласно схеме `Quiz`.
3. `id` созданного квиза уникален.
4. Поля, для которых в `CreateQuizRequest` есть значения по умолчанию (`fingerprinting`, `repeatable`, `note_prevented`, `status`, `pausable`, `super`), установлены в эти значения по умолчанию, если они не были переданы в запросе.
5. Сервер-генерируемые поля (`qid`, `accountid`, `version`, `created_at`, `updated_at`, счетчики) корректно заполнены.
6. Поле `status` в ответе соответствует запрошенному или значению по умолчанию (`draft`).
#### 18.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации (согласно `#/components/responses/UnauthorizedError`).
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 18.3.3 Проверка валидации данных (Ответ 400 Bad Request или 422 Unprocessable Entity)
**Сценарий 3.1: Отсутствие обязательных полей в `CreateQuizRequest`**
- Входные данные: Тело запроса без обязательного поля `name`.
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на отсутствующее поле `name` (для 422, тело ответа должно содержать `message` с деталями).
**Сценарий 3.2: Некорректный формат данных в полях `CreateQuizRequest`**
- Входные данные (примеры):
- `name` длиннее 700 символов.
- `fingerprinting` не boolean (например, строка "true").
- `status` не из списка enum (`draft`, `template`, `stop`, `start`).
- `limit` не integer или отрицательное.
- `due_to` не integer (timestamp).
- `config` - невалидный JSON в строке.
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на поля с некорректным форматом или значением.
#### 18.3.4 Проверка ответа 406 Not Acceptable
**Сценарий 4.1: Условия, приводящие к 406 Not Acceptable**
- Предусловия: (Необходимо определить конкретные условия на основе спецификации `StatusNotAcceptableError`. Например, попытка создать квиз с параметрами, которые несовместимы с текущими настройками аккаунта или системы).
- Входные данные: Корректный запрос на создание квиза, но выполнены условия для ответа 406.
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusNotAcceptableError` и содержит информативное сообщение.
#### 18.3.5 Проверка ответа 409 Conflict
**Сценарий 5.1: Создание квиза с уже существующим уникальным идентификатором (если применимо)**
- Предусловия: (Необходимо определить, какие поля, кроме `id`, должны быть уникальными. Например, если `name` должно быть уникальным в рамках аккаунта). Попытка создать квиз с `name`, который уже существует у другого квиза этого же пользователя.
- Входные данные: Запрос на создание квиза с `name`, которое уже занято.
- Ожидаемый результат: 409 Conflict.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusConflictError` и содержит информативное сообщение.
#### 18.3.6 Проверка безопасности
**Сценарий 6.1: SQL-инъекции**
- Входные данные: В строковых полях запроса (`name`, `description`, `config`) передаются SQL-инъекции.
- Ожидаемый результат: 400/422, либо запрос обработан без выполнения инъекции, данные сохранены корректно (экранированы).
- Проверка: Отсутствие уязвимости к SQL-инъекциям.
**Сценарий 6.2: XSS-атаки**
- Входные данные: В строковых полях запроса (`name`, `description`, `config`) передаются XSS-скрипты.
- Ожидаемый результат: 400/422, либо данные сохранены корректно (экранированы), и XSS не выполняется при отображении этих данных.
- Проверка: Отсутствие уязвимости к XSS.
#### 18.3.7 Проверка производительности
**Сценарий 7.1: Время создания квиза**
- Проверка: Время ответа на запрос создания квиза < N ms (например, < 500ms).
- Проверка: Стабильность времени ответа при многократных запросах.
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: Система способна обработать X запросов на создание квизов в секунду/минуту без значительного ухудшения производительности или ошибок.
#### 18.3.8 Обработка ошибок (Общие)
**Сценарий 8.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация непредвиденного сбоя на сервере при обработке запроса.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа соответствует схеме `#/components/responses/InternalServerError`.
### 18.4 Особые моменты для тестирования
1. **Валидация схемы `CreateQuizRequest`:**
* Тщательная проверка всех обязательных (`name`) и опциональных полей.
* Проверка типов данных и ограничений (например, `name` maxLength: 700, `status` enum).
* Проверка обработки значений по умолчанию для опциональных полей.
* Валидация JSON-структуры внутри строки `config`.
2. **Логика статусов квиза (`status`):**
* Корректное создание квиза с каждым из допустимых статусов (`draft`, `template`, `stop`, `start`).
* Проверка статуса по умолчанию (`draft`), если не передан.
3. **Влияние `question_cnt`:** Если `question_cnt` предназначен для автоматического создания пустого набора вопросов, проверить, что это происходит. Если это просто метаданные, проверить, что поле сохраняется. (В схеме `Quiz` есть поле `questions_count`, которое может быть связано).
4. **Настройки квиза:** Проверить, как различные булевы флаги (`fingerprinting`, `repeatable`, `note_prevented`, `mail_notifications`, `unique_answers`, `pausable`, `super`) влияют на созданный квиз и его последующее поведение (может требовать интеграционных тестов).
5. **Связь с `group_id`:** Если `super: true`, то `group_id` может быть `null`. Если `super: false` и квиз является частью группы, `group_id` должен указывать на существующий "супер-квиз". Проверить логику валидации этих полей.
### 18.5 Критерии приемки
1. Все тестовые сценарии из раздела 18.3 пройдены успешно.
2. Время ответа на создание квиза соответствует установленным требованиям производительности.
3. Обработка всех кодов ошибок (201, 400, 401, 406, 409, 422, 500) соответствует спецификации API и предоставляет корректные сообщения.
4. Обеспечена защита от основных уязвимостей безопасности (SQL-инъекции, XSS).
5. Созданный квиз корректно сохраняется в базе данных со всеми указанными атрибутами и доступен для последующего использования.
6. Логирование операций по созданию квизов информативно и покрывает как успешные случаи, так и ошибки.
7. Значения по умолчанию для полей `CreateQuizRequest` применяются корректно.
## 19. POST /quiz/getList
### 19.1 Описание
Получение списка квизов с поддержкой пагинации и расширенной фильтрацией. Эндпоинт предназначен для отображения квизов в различных представлениях, учитывая их статус, временные рамки, принадлежность к группам и другие атрибуты.
### 19.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetQuizListRequest`.
- В случае успеха возвращает список квизов и метаданные пагинации согласно схеме `#/components/schemas/GetQuizListResponse` (статус 200 OK).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 500 (Internal Server Error).
### 19.3 Тестовые сценарии
#### 19.3.1 Успешное получение списка квизов (с пагинацией и фильтрами)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- В системе существуют квизы, соответствующие критериям запроса.
**Входные данные:**
- Метод: POST
- URL: /quiz/getList
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (пример, все поля опциональны, используя значения по умолчанию, если не указаны `limit: 10`, `page: 1`):
```json
{
"limit": 5,
"page": 1,
"from": 1672531200, // Пример timestamp (1 Jan 2023)
"to": 1675209600, // Пример timestamp (1 Feb 2023)
"search": "география",
"status": "start",
"deleted": false,
"archived": false,
"super": false,
"group_id": null
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (согласно `#/components/schemas/GetQuizListResponse` и `#/components/schemas/Quiz`):
```json
{
"count": 20, // integer, uint64 - Общее количество квизов, соответствующих фильтрам
"items": [
{
"id": 101,
"qid": "uuid-quiz-101",
"accountid": "user-account-id",
"deleted": false,
"archived": false,
"fingerprinting": true,
"repeatable": true,
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"name": "Квиз по географии Европы",
"description": "Проверьте свои знания о странах Европы.",
"config": "{\"rules\":{}}",
"status": "start",
"limit": 50,
"due_to": 1675000000,
"time_of_passing": 1800,
"pausable": true,
"version": 2,
"version_comment": "Обновлены вопросы",
"parent_ids": [],
"created_at": "2023-01-15T10:00:00Z",
"updated_at": "2023-01-20T11:30:00Z",
"questions_count": 15,
"session_count": 150,
"passed_count": 120,
"average_time": 1200,
"super": false,
"group_id": null
}
// ... другие квизы, если count > 1 и limit позволяет
]
}
```
**Проверки:**
1. Ответ содержит корректное поле `count`, отражающее общее количество квизов по заданным фильтрам.
2. Количество квизов в массиве `items` не превышает запрошенный `limit` (или значение по умолчанию 10).
3. Пагинация работает корректно: при изменении `page` возвращаются соответствующие срезы данных.
4. Каждый объект квиза в массиве `items` соответствует схеме `Quiz` и содержит все ожидаемые поля с корректными типами данных.
5. Если применялись фильтры (`from`/`to`, `search`, `status`, `deleted`, `archived`, `super`, `group_id`), то все возвращенные квизы соответствуют этим фильтрам.
6. Поля `limit` и `page` по умолчанию равны 10 и 1 соответственно, если не указаны в запросе.
#### 19.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа содержит сообщение об ошибке авторизации (согласно `#/components/responses/UnauthorizedError`).
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 19.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
(Большинство полей в `GetQuizListRequest` опциональны. Проверки касаются корректности форматов и значений, если поля предоставлены.)
**Сценарий 3.1: Некорректные параметры пагинации**
- Входные данные: `page` или `limit` не являются integer (uint64), или имеют некорректный формат/значение (например, `limit: 0`, `page: 0`, если `minimum: 1`).
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на невалидные параметры пагинации.
**Сценарий 3.2: Некорректный формат фильтров**
- Входные данные (примеры):
- `from` или `to` не являются integer (int64).
- `status` не из списка допустимых enum значений (`stop`, `start`, `draft`, `template`, `timeout`, `offlimit`).
- `deleted`, `archived`, `super` не являются boolean.
- `group_id` не integer (uint64).
- `from` > `to` (некорректный временной диапазон).
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на невалидные параметры фильтрации.
#### 19.3.4 Проверка ответа 406 Not Acceptable
**Сценарий 4.1: Условия, приводящие к 406 Not Acceptable**
- Предусловия: (Необходимо определить конкретные условия на основе спецификации `StatusNotAcceptableError`. Например, запрос на получение данных в формате, который сервер не может предоставить, или комбинация фильтров, которая является недопустимой по бизнес-логике).
- Входные данные: Корректный запрос на получение списка, но выполнены условия для ответа 406.
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа соответствует схеме `#/components/responses/StatusNotAcceptableError`.
#### 19.3.5 Проверка производительности
**Сценарий 5.1: Время ответа на получение списка**
- Проверка: Время ответа < N ms (например, < 1000ms) при запросе стандартной страницы с `limit=10` и без сложных фильтров.
- Проверка: Время ответа при запросе с большим количеством фильтров и на разные страницы, особенно при большом общем количестве квизов.
**Сценарий 5.2: Нагрузочное тестирование**
- Проверка: Система способна обработать X запросов на получение списка квизов в секунду/минуту без значительного ухудшения производительности или ошибок.
#### 19.3.6 Обработка ошибок (Общие)
**Сценарий 6.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация непредвиденного сбоя на сервере (например, ошибка подключения к БД при выборке данных).
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа соответствует схеме `#/components/responses/InternalServerError`.
### 19.4 Особые моменты для тестирования
1. **Логика пагинации (`limit`, `page`):**
* Корректность `count` при наличии/отсутствии фильтров.
* Правильность выдачи данных на первой, последней и промежуточных страницах.
* Поведение при запросе страницы, превышающей общее количество страниц (ожидается `items` - пустой массив, `page` равен запрошенному, `count` корректен).
* Проверка значений по умолчанию для `limit` (10) и `page` (1).
* Проверка минимальных значений `limit: 1`, `page: 1`.
2. **Логика фильтрации (согласно `GetQuizListRequest`):**
* Корректность работы каждого отдельного фильтра:
- `from`/`to`: выборка квизов, созданных/измененных в указанном временном диапазоне (проверить по `created_at` или `updated_at` в зависимости от логики).
- `search`: поиск по релевантным текстовым полям квиза (например, `name`, `description`). Проверить чувствительность к регистру, поиск по части слова.
- `status`: выборка квизов только с указанным статусом.
- `deleted`: выборка только удаленных (`true`) или только неудаленных (`false`/отсутствие) квизов.
- `archived`: выборка только архивированных (`true`) или только неархивированных (`false`/отсутствие) квизов.
- `super`: выборка "супер-квизов" (`true`) или обычных (`false`/отсутствие).
- `group_id`: выборка квизов, принадлежащих указанной группе.
* Корректность работы комбинации нескольких фильтров одновременно.
* Поведение при передаче несуществующих значений для фильтров (например, `group_id`, которого нет).
3. **Сортировка (если поддерживается):**
* Проверка сортировки по умолчанию (например, по дате создания).
* Если API позволяет задавать параметры сортировки, протестировать их.
4. **Полнота данных в ответе:**
* Каждый квиз в списке `items` должен содержать все поля, определенные схемой `Quiz`.
### 19.5 Критерии приемки
1. Все тестовые сценарии из раздела 19.3 пройдены успешно.
2. Время ответа на получение списка квизов соответствует требованиям производительности.
3. Пагинация и все заявленные фильтры работают корректно.
4. Обработка всех кодов ошибок (200, 400, 401, 406, 500) соответствует спецификации API.
5. Данные в ответе полны, корректны и соответствуют схемам `GetQuizListResponse` и `Quiz`.
6. Логирование запросов на получение списка квизов информативно.
## 20. PATCH /quiz/edit
### 20.1 Описание
Обновление существующего квиза. Позволяет изменять одно или несколько полей квиза по его ID. Используется для модификации настроек, статуса, контента или метаданных квиза.
### 20.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: PATCH.
- Тело запроса должно соответствовать схеме `#/components/schemas/UpdateQuizRequest` и содержать обязательное поле `id` квиза.
- В случае успеха (200 OK) возвращает объект согласно схеме `#/components/schemas/UpdateQuizResponse`, содержащий `updated` (ID обновленного квиза).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable), 409 (Conflict), 422 (Unprocessable Entity), 424 (Failed Dependency), 500 (Internal Server Error).
### 20.3 Тестовые сценарии
#### 20.3.1 Успешное обновление квиза (изменение нескольких полей)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Квиз с указанным `id` существует в системе.
**Входные данные:**
- Метод: PATCH
- URL: /quiz/edit
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/UpdateQuizRequest`):
```json
{
"id": 101, // integer, uint64 - ID существующего квиза
"name": "Обновленное название квиза",
"desc": "Новое детальное описание для квиза.",
"status": "start",
"limit": 150,
"fp": true,
"rep": false,
"conf": "{}"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (согласно `#/components/schemas/UpdateQuizResponse`):
```json
{
"updated": 101 // integer, uint64 - ID обновленного квиза
}
```
**Проверки:**
1. Поле `updated` в ответе содержит `id` обновленного квиза.
2. Данные квиза в базе данных (или при последующем GET запросе квиза) обновлены в соответствии с переданными в запросе полями.
Соответствие полей `UpdateQuizRequest` полям `Quiz`:
- `fp` -> `fingerprinting`
- `rep` -> `repeatable`
- `note_prevented` -> `note_prevented`
- `mailing` -> `mail_notifications`
- `uniq` -> `unique_answers`
- `name` -> `name`
- `desc` -> `description`
- `conf` -> `config`
- `status` -> `status` (enum: [draft, template, stop, start])
- `limit` -> `limit`
- `due_to` -> `due_to`
- `time_of_passing` -> `time_of_passing`
- `pausable` -> `pausable`
- `question_cnt` -> `questions_count` (или другая логика, если `question_cnt` изменяет количество вопросов)
- `super` -> `super`
- `group_id` -> `group_id`
3. Поля, которые не были переданы в запросе на обновление, остались без изменений в базе данных.
4. Поле `updated_at` для квиза в базе данных обновлено.
#### 20.3.2 Успешное обновление одного поля квиза
**Предусловия:**
- Пользователь авторизован, JWT токен валиден.
- Квиз с `id` = 102 существует.
**Входные данные:**
- Метод: PATCH
- URL: /quiz/edit
- Headers: Authorization: Bearer {valid_jwt_token}, Content-Type: application/json
- Тело запроса:
```json
{
"id": 102,
"status": "stop"
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Тело ответа: `{"updated": 102}`
**Проверки:**
1. `updated` в ответе равен 102.
2. В базе данных для квиза с `id`=102 обновлен только `status`, остальные поля не изменились.
3. `updated_at` обновлен.
#### 20.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request или 422 Unprocessable Entity)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса без поля `id`.
```json
{ "name": "Запрос без ID" }
```
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на отсутствие или невалидность поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{ "id": "not_an_integer", "name": "Невалидный ID" }
```
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 3.3: Попытка обновления несуществующего квиза**
- Входные данные: `id` = 99999 (несуществующий).
```json
{ "id": 99999, "name": "Несуществующий квиз" }
```
- Ожидаемый результат: 400, 422 (или 404, если бы был заявлен и сервер так реагировал).
- Проверка: Сообщение об ошибке указывает, что квиз с таким `id` не найден или `id` невалиден.
**Сценарий 3.4: Некорректные значения для обновляемых полей**
- Входные данные (примеры, тестировать каждое поле отдельно):
- `name` длиннее 700 символов.
- `status` не из списка допустимых enum значений (`draft`, `template`, `stop`, `start`).
- `fp`, `rep`, `note_prevented`, `mailing`, `uniq`, `pausable`, `super` не boolean.
- `limit`, `due_to`, `time_of_passing`, `question_cnt`, `group_id` не integer/uint64 или отрицательные, если не разрешено.
- `conf` не является валидной JSON-строкой.
- Ожидаемый результат: 400 Bad Request или 422 Unprocessable Entity.
- Проверка: Сообщение об ошибке указывает на конкретное поле и характер ошибки валидации (для 422, тело ответа может содержать `message` с деталями).
#### 20.3.4 Проверка авторизации
**Сценарий 4.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 4.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 4.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 20.3.5 Проверка специфических кодов ответа
**Сценарий 5.1: Условия для 406 Not Acceptable**
- Предусловия: (Определить условия на основе спецификации `StatusNotAcceptableError`. Например, попытка изменить квиз, который заблокирован для редактирования из-за его статуса или активных сессий).
- Ожидаемый результат: 406 Not Acceptable.
- Проверка: Тело ответа согласно `#/components/responses/StatusNotAcceptableError`.
**Сценарий 5.2: Условия для 409 Conflict**
- Предусловия: (Определить условия на основе спецификации `StatusConflictError`. Например, попытка установить `name`, которое уже используется другим квизом, если `name` должно быть уникальным).
- Ожидаемый результат: 409 Conflict.
- Проверка: Тело ответа согласно `#/components/responses/StatusConflictError`.
**Сценарий 5.3: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой внешней системы, от которой зависит обновление данных квиза, например, при обновлении счетчиков вопросов).
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 20.3.6 Проверка безопасности (SQL-инъекции, XSS)
**Сценарий 6.1: SQL-инъекции в строковых полях**
- Входные данные: В полях `name`, `desc`, `conf` передаются SQL-инъекции.
- Ожидаемый результат: 400/422, либо запрос обработан без выполнения инъекции.
- Проверка: Отсутствие уязвимости.
**Сценарий 6.2: XSS-атаки в строковых полях**
- Входные данные: В полях `name`, `desc`, `conf` передаются XSS-скрипты.
- Ожидаемый результат: 400/422, либо данные корректно сохранены/обновлены (экранированы), XSS не выполняется при отображении.
- Проверка: Отсутствие уязвимости.
#### 20.3.7 Проверка производительности
**Сценарий 7.1: Время обновления квиза**
- Проверка: Время ответа на запрос обновления < N ms (например, < 500ms).
**Сценарий 7.2: Нагрузочное тестирование**
- Проверка: Система выдерживает X одновременных запросов на обновление различных квизов.
#### 20.3.8 Обработка ошибок (Общие)
**Сценарий 8.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация сбоя на сервере при обновлении (например, ошибка БД).
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 20.4 Особые моменты для тестирования
1. **Частичное обновление**: Убедиться, что при обновлении только некоторых полей остальные поля квиза не затрагиваются и сохраняют свои предыдущие значения.
2. **Валидация `id`**: Тщательная проверка того, что `id` существует и принадлежит текущему пользователю/аккаунту (если есть такая логика авторизации на уровне записи).
3. **Валидация всех полей `UpdateQuizRequest`**: Проверить каждое поле на соответствие типам, форматам и допустимым значениям.
4. **Обновление `status`**: Проверить корректность перехода между различными статусами квиза (`draft`, `template`, `stop`, `start`) и возможные ограничения (например, можно ли перевести активный квиз в `draft`).
5. **Поле `updated_at`**: Проверить, что это поле автоматически обновляется при любом успешном изменении квиза.
6. **Влияние `question_cnt`**: Если это поле предназначено для изменения количества связанных вопросов (например, добавление/удаление пустых слотов), проверить эту логику. Если это просто обновление метаданных, убедиться, что значение `questions_count` в объекте `Quiz` обновляется соответствующим образом или поле `question_cnt` просто сохраняется.
7. **Логика `super` и `group_id`**: Проверить корректность обновления этих полей и их взаимное влияние.
### 20.5 Критерии приемки
1. Все тестовые сценарии из раздела 20.3 пройдены успешно.
2. Квиз корректно обновляется в базе данных, затрагиваются только переданные поля.
3. Обработка всех кодов ошибок (200, 400, 401, 406, 409, 422, 424, 500) соответствует спецификации API.
4. Обеспечена защита от основных уязвимостей безопасности.
5. Поле `updated_at` квиза обновляется корректно.
6. Логирование операций по обновлению квизов информативно.
## 21. POST /quiz/copy
### 21.1 Описание
Создание копии существующего квиза. Новый квиз создается со всеми настройками и атрибутами оригинального квиза, но получает новый уникальный идентификатор (`id`, `qid`) и некоторые поля сбрасываются или устанавливаются в начальные значения (например, версия, счетчики прохождений, даты создания/обновления).
### 21.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/CopyQuizRequest` и содержать обязательное поле `id` (ID оригинального квиза).
- В случае успеха (200 OK) возвращает полный объект нового, скопированного квиза согласно схеме `#/components/schemas/Quiz`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 21.3 Тестовые сценарии
#### 21.3.1 Успешное копирование квиза
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Существует квиз с `id`, указанным в запросе (оригинальный квиз).
**Входные данные:**
- Метод: POST
- URL: /quiz/copy
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/CopyQuizRequest`):
```json
{
"id": 101 // integer, uint64 - ID существующего квиза для копирования
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (новый скопированный квиз, согласно `#/components/schemas/Quiz`. Пример):
```json
{
"id": 202, // integer, uint64 - НОВЫЙ ID для скопированного квиза
"qid": "new-uuid-for-quiz-202", // string - НОВЫЙ UUID
"accountid": "user-account-id", // string - ID аккаунта создателя (копируется)
"deleted": false, // boolean - всегда false для новой копии
"archived": false, // boolean - всегда false для новой копии
"fingerprinting": true, // boolean - копируется из оригинала
"repeatable": true, // boolean - копируется из оригинала
"note_prevented": false, // boolean - копируется из оригинала
"mail_notifications": true, // boolean - копируется из оригинала
"unique_answers": false, // boolean - копируется из оригинала
"name": "Копия: Оригинальное название квиза", // string - name оригинала, возможно с префиксом "Копия: "
"description": "Описание оригинального квиза.", // string - копируется из оригинала
"config": "{}", // string - копируется из оригинала
"status": "draft", // string, enum - статус новой копии (вероятно, 'draft' по умолчанию)
"limit": 50, // integer, uint64 - копируется из оригинала
"due_to": 1675000000, // integer, uint64 - копируется из оригинала
"time_of_passing": 1800, // integer, uint64 - копируется из оригинала
"pausable": true, // boolean - копируется из оригинала
"version": 1, // integer - версия сбрасывается до 1 для новой копии
"version_comment": null, // string - комментарий к версии сбрасывается
"parent_ids": [], // array of integers - parent_ids сбрасываются или копируются (уточнить логику, скорее всего сбрасываются)
"created_at": "YYYY-MM-DDTHH:mm:ssZ", // string, date-time - НОВАЯ дата создания
"updated_at": "YYYY-MM-DDTHH:mm:ssZ", // string, date-time - НОВАЯ дата обновления (равна created_at)
"questions_count": 0, // integer, uint64 - счетчик вопросов (копируется от оригинала или сбрасывается - уточнить, скорее всего копируется, но вопросы сами не копируются этой операцией)
"session_count": 0, // integer, uint64 - сбрасывается до 0
"passed_count": 0, // integer, uint64 - сбрасывается до 0
"average_time": 0, // integer, uint64 - сбрасывается до 0
"super": false, // boolean - копируется из оригинала
"group_id": null // integer, uint64 - копируется из оригинала (если был)
}
```
**Проверки:**
1. В базе данных создан новый квиз.
2. Возвращенный объект квиза имеет новый, уникальный `id` и `qid`.
3. Поля `accountid`, `fingerprinting`, `repeatable`, `note_prevented`, `mail_notifications`, `unique_answers`, `name` (с возможным префиксом), `description`, `config`, `limit`, `due_to`, `time_of_passing`, `pausable`, `super`, `group_id`, `questions_count` (значение копируется, но не сами вопросы) скопированы из оригинального квиза.
4. Поле `status` у новой копии установлено в ожидаемое значение (например, `draft`).
5. Поля `deleted` и `archived` у новой копии установлены в `false`.
6. Поле `version` у новой копии установлено в `1`, а `version_comment` - `null` (или пустое).
7. Поля `session_count`, `passed_count`, `average_time` у новой копии сброшены в `0`.
8. Поля `created_at` и `updated_at` у новой копии являются свежими временными метками.
9. Оригинальный квиз (с `id` из запроса) остался без изменений в базе данных.
10. Поле `parent_ids` для копии либо пустое, либо содержит скопированные значения (уточнить ожидаемое поведение).
#### 21.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 21.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса - пустой JSON `{}`.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{ "id": "not_an_integer" }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 3.3: Копирование несуществующего квиза**
- Входные данные: `id` = 99999 (несуществующий оригинальный квиз).
```json
{ "id": 99999 }
```
- Ожидаемый результат: 400 Bad Request (или 404, если бы он был заявлен и API так отвечал бы).
- Проверка: Сообщение об ошибке указывает, что оригинальный квиз с таким `id` не найден.
#### 21.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой при записи в базу данных нового квиза из-за внешних проблем, не связанных с валидацией входных данных).
- Входные данные: Корректный запрос на копирование.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 21.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере в процессе копирования.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 21.4 Особые моменты для тестирования
1. **Генерация новых идентификаторов**: Убедиться, что у скопированного квиза всегда генерируются новые, уникальные `id` и `qid`.
2. **Копирование атрибутов**: Детально проверить, какие именно поля копируются из оригинала, какие устанавливаются по умолчанию (`deleted`, `archived`, `status`), а какие сбрасываются (`version`, `version_comment`, счетчики статистики `session_count`, `passed_count`, `average_time`).
3. **Имя копии**: Уточнить, добавляется ли префикс (например, "Копия: ") к полю `name` скопированного квиза, или имя копируется без изменений.
4. **Временные метки**: `created_at` и `updated_at` для копии должны быть новыми.
5. **Иммутабельность оригинала**: Оригинальный квиз не должен быть затронут операцией копирования.
6. **Связанные сущности (вопросы)**: Этот эндпоинт, скорее всего, копирует только сам "контейнер" квиза. Убедиться, что вопросы из оригинального квиза не копируются автоматически вместе с квизом этой операцией (их нужно будет добавлять/копировать отдельно). Поле `questions_count` в копии должно отражать это (если `questions_count` оригинала копируется, это может быть ожидаемым поведением, но сами вопросы не должны быть связаны с новой копией без отдельных действий).
7. **Статус копии**: Убедиться, что новый квиз создается в предопределенном статусе (например, `draft`), независимо от статуса оригинала.
8. **Поле `parent_ids`**: Уточнить, копируется ли это поле, или оно всегда сбрасывается для копии.
### 21.5 Критерии приемки
1. Все тестовые сценарии из раздела 21.3 пройдены успешно.
2. Новый квиз корректно создается в базе данных как копия оригинала, с соответствующими изменениями (новый ID, qid, статус, сброшенные счетчики, свежие timestamp'ы).
3. Оригинальный квиз остается неизменным.
4. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
5. Логирование операций по копированию квизов информативно.
## 22. POST /quiz/history
### 22.1 Описание
Получение истории изменений для указанного квиза. Каждая запись в истории представляет собой состояние (версию) квиза на определенный момент времени. Эндпоинт возвращает массив версий квиза.
### 22.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetQuizHistoryRequest` и содержать обязательное поле `id` квиза, а также опциональные поля для пагинации `l` (limit) и `p` (page).
- В случае успеха (200 OK) возвращает массив объектов `Quiz`, где каждый элемент - это одна версия из истории квиза.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 22.3 Тестовые сценарии
#### 22.3.1 Успешное получение истории квиза (с пагинацией)
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Квиз с указанным `id` существует и имеет несколько записей в истории (несколько версий).
**Входные данные:**
- Метод: POST
- URL: /quiz/history
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса (согласно `#/components/schemas/GetQuizHistoryRequest`):
```json
{
"id": 101, // integer, uint64 - ID квиза
"l": 5, // integer, uint64 - Limit (количество записей на странице)
"p": 1 // integer, uint64 - Page (номер страницы, например, 1-based)
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа (массив объектов `Quiz`):
```json
[
{
"id": 101, // integer, uint64 - ID оригинального квиза
"qid": "uuid-quiz-101",
"accountid": "user-account-id",
"deleted": false,
"archived": false,
"fingerprinting": true,
"repeatable": false, // Значение из Версии 2
"note_prevented": false,
"mail_notifications": true,
"unique_answers": false,
"name": "Обновленное название квиза (Версия 2)",
"description": "Новое описание для квиза (Версия 2).",
"config": "{\"rules\":{\"v2\":true}}",
"status": "start", // Статус из Версии 2
"limit": 150,
"due_to": 1680000000,
"time_of_passing": 3600,
"pausable": false,
"version": 2, // integer - номер версии
"version_comment": "Вторая редакция, изменен статус и лимиты",
"parent_ids": [],
"created_at": "2023-01-15T10:00:00Z", // Время создания оригинального квиза
"updated_at": "2023-01-20T14:30:00Z", // Время фиксации этой версии
"questions_count": 12,
"session_count": 25, // Статистика может быть специфична для версии или общей (уточнить)
"passed_count": 10,
"average_time": 1500,
"super": false,
"group_id": null
},
{
"id": 101, // ID оригинального квиза
"qid": "uuid-quiz-101",
"accountid": "user-account-id",
// ... остальные поля для Версии 1 ...
"name": "Начальное название квиза (Версия 1)",
"status": "draft", // Статус из Версии 1
"version": 1,
"version_comment": "Первая версия",
"updated_at": "2023-01-15T10:00:00Z" // Время фиксации этой версии
}
// ... другие версии, если l > 2 ...
]
```
**Проверки:**
1. Ответ является JSON массивом.
2. Каждый элемент массива является полным объектом `Quiz`, представляющим состояние квиза на момент этой версии.
3. Количество элементов в массиве не превышает значение `l` (limit) из запроса.
4. Если `p` (page) и `l` (limit) используются, пагинация работает корректно, возвращая соответствующий срез истории.
5. Поле `version` в каждой записи истории корректно отражает номер версии (или другое поле, идентифицирующее версию, например, `updated_at` этой записи истории).
6. Записи истории отсортированы в ожидаемом порядке (например, по убыванию `version` или `updated_at` этой записи истории).
7. Если у квиза нет истории (например, он только что создан и не изменялся), массив содержит одну запись, представляющую текущее состояние квиза с `version: 1`.
8. Если запрос сделан с `l=0` или без `l` и `p`, проверить поведение по умолчанию (например, возврат всех версий или первой страницы с лимитом по умолчанию).
9. Поле `id` во всех объектах истории одинаково и соответствует `id` запрошенного квиза.
#### 22.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 22.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса без поля `id`.
```json
{ "l": 10, "p": 1 }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`, `l`, или `p`**
- Входные данные (примеры):
- `id` как строка: `{ "id": "not_an_integer", "l": 10, "p": 1 }`
- `l` как строка: `{ "id": 101, "l": "ten", "p": 1 }`
- `p` как строка: `{ "id": 101, "l": 10, "p": "one" }`
- `l` или `p` отрицательные (если не разрешено).
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных или недопустимое значение для соответствующего поля.
**Сценарий 3.3: Запрос истории для несуществующего квиза**
- Входные данные: `id` = 99999 (несуществующий квиз).
```json
{ "id": 99999, "l": 10, "p": 1 }
```
- Ожидаемый результат: 200 OK и пустой массив `[]` в теле ответа (или 404, если бы он был заявлен, но OpenAPI указывает на 200 для успешного, даже если данных нет, или 400 для ошибки запроса).
- Проверка: Если 200 OK, тело ответа - пустой массив. Если 400, сообщение указывает, что квиз не найден.
#### 22.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Определить условия на основе спецификации `StatusFailedDependencyError`. Например, сбой при чтении из базы данных истории).
- Входные данные: Корректный запрос на получение истории.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 22.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 22.4 Особые моменты для тестирования
1. **Порядок версий**: Убедиться, что версии в истории отсортированы последовательно и предсказуемо (например, по убыванию номера `version` или по убыванию `updated_at` записи истории).
2. **Полнота данных в версиях**: Каждая версия квиза в массиве должна содержать все поля из схемы `Quiz` и отражать состояние квиза на момент создания этой версии.
3. **Пагинация (`l` и `p`)**: Тщательно проверить работу пагинации: корректность количества возвращаемых элементов, переход по страницам, поведение на первой и последней странице, поведение при запросе страницы за пределами существующей истории.
4. **История для нового/неизмененного квиза**: Если квиз только создан и не редактировался, эндпоинт должен вернуть массив с одной записью (текущее состояние, `version: 1`).
5. **Глубина/ограничения истории**: Если система накладывает ограничения на количество хранимых версий или глубину истории, это необходимо проверить.
6. **Согласованность данных**: Поля `id`, `qid`, `accountid` в объектах истории должны соответствовать оригинальному квизу. Поле `version` должно быть уникальным для каждой записи истории одного квиза и увеличиваться (или изменяться предсказуемо).
7. **`created_at` vs `updated_at` в истории**: Убедиться, что `created_at` в записях истории остается временем создания самого квиза, а `updated_at` отражает время сохранения конкретной версии.
### 22.5 Критерии приемки
1. Все тестовые сценарии из раздела 22.3 пройдены успешно.
2. История квиза возвращается корректно, со всеми версиями и точными данными для каждой версии.
3. Параметры пагинации `l` и `p` работают правильно.
4. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
5. Данные в ответе структурированы правильно (массив объектов `Quiz`).
6. Логирование запросов к истории квизов информативно.
## 23. DELETE /quiz/delete
### 23.1 Описание
Удаление (деактивация) квиза по его идентификатору. Операция требует авторизации и принимает в теле запроса объект с обязательным полем `id` (ID квиза для удаления).
### 23.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: DELETE.
- Тело запроса должно соответствовать схеме `#/components/schemas/DeleteQuizRequest` и содержать обязательное поле `id` (ID квиза).
- В случае успеха (200 OK) возвращает объект с полем `deactivated` (ID деактивированного квиза) согласно схеме `#/components/schemas/DeleteQuizResponse`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 23.3 Тестовые сценарии
#### 23.3.1 Успешное удаление квиза
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Существует квиз с указанным `id`, и он не удалён.
**Входные данные:**
- Метод: DELETE
- URL: /quiz/delete
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{ "id": 101 }
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{ "deactivated": 101 }
```
**Проверки:**
1. Квиз с указанным `id` помечен как удалённый (`deleted: true`) в базе данных.
2. Возвращённое значение `deactivated` совпадает с `id` удалённого квиза.
3. Повторный запрос на удаление этого же квиза возвращает 200 OK и тот же результат (операция идемпотентна).
4. Квиз больше не отображается в списках активных квизов (например, в /quiz/getList без фильтра deleted=true).
5. Связанные вопросы и результаты также помечены как удалённые (если реализовано каскадное удаление).
#### 23.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 23.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса - пустой JSON `{}`.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{ "id": "not_an_integer" }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 3.3: Удаление несуществующего квиза**
- Входные данные: `id` = 99999 (несуществующий квиз).
```json
{ "id": 99999 }
```
- Ожидаемый результат: 200 OK (или 400/404, если реализовано иначе).
- Проверка: Если 200 OK — операция идемпотентна, если 400/404 — сообщение указывает, что квиз не найден.
#### 23.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Имитация сбоя внешней системы или базы данных при удалении квиза).
- Входные данные: Корректный запрос на удаление.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 23.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 23.4 Особые моменты для тестирования
1. **Идемпотентность**: Повторное удаление одного и того же квиза не приводит к ошибке.
2. **Каскадное удаление**: Проверить, что связанные вопросы и результаты также помечаются как удалённые (если реализовано).
3. **Видимость**: Удалённый квиз не отображается в списках активных квизов.
4. **Восстановление**: Если предусмотрено восстановление, проверить возможность восстановления удалённого квиза.
5. **Логирование**: Проверить запись операции удаления в логах.
6. **Мониторинг**: Проверить метрики и алерты по операциям удаления.
### 23.5 Критерии приемки
1. Все тестовые сценарии из раздела 23.3 пройдены успешно.
2. Квиз корректно помечается как удалённый в базе данных.
3. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
4. Операция удаления идемпотентна.
5. Логирование и мониторинг операций удаления работают корректно.
## 24. PATCH /quiz/archive
### 24.1 Описание
Архивация квиза по его идентификатору. Операция требует авторизации и принимает в теле запроса объект с обязательным полем `id` (ID квиза для архивации).
### 24.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: PATCH.
- Тело запроса должно соответствовать схеме `#/components/schemas/DeleteQuizRequest` и содержать обязательное поле `id` (ID квиза).
- В случае успеха (200 OK) возвращает объект с полем `deactivated` (ID заархивированного квиза) согласно схеме `#/components/schemas/DeleteQuizResponse`.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 424 (Failed Dependency), 500 (Internal Server Error).
### 24.3 Тестовые сценарии
#### 24.3.1 Успешная архивация квиза
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Существует квиз с указанным `id`, и он не заархивирован.
**Входные данные:**
- Метод: PATCH
- URL: /quiz/archive
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{ "id": 101 }
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{ "deactivated": 101 }
```
**Проверки:**
1. Квиз с указанным `id` помечен как заархивированный (`archived: true`) в базе данных.
2. Возвращённое значение `deactivated` совпадает с `id` заархивированного квиза.
3. Повторный запрос на архивацию этого же квиза возвращает 200 OK и тот же результат (операция идемпотентна).
4. Квиз больше не отображается в списках активных квизов (например, в /quiz/getList без фильтра archived=true).
5. Связанные вопросы и результаты остаются без изменений (если не реализовано каскадное архивирование).
#### 24.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
**Сценарий 2.3: Истекший токен**
- Headers: Authorization: Bearer expired_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 24.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
**Сценарий 3.1: Отсутствие обязательного поля `id`**
- Входные данные: Тело запроса - пустой JSON `{}`.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательного поля `id`.
**Сценарий 3.2: Некорректный тип данных для `id`**
- Входные данные: `id` передано как строка.
```json
{ "id": "not_an_integer" }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных для `id`.
**Сценарий 3.3: Архивация несуществующего квиза**
- Входные данные: `id` = 99999 (несуществующий квиз).
```json
{ "id": 99999 }
```
- Ожидаемый результат: 200 OK (или 400/404, если реализовано иначе).
- Проверка: Если 200 OK — операция идемпотентна, если 400/404 — сообщение указывает, что квиз не найден.
#### 24.3.4 Проверка специфических кодов ответа
**Сценарий 4.1: Условия для 424 Failed Dependency**
- Предусловия: (Имитация сбоя внешней системы или базы данных при архивации квиза).
- Входные данные: Корректный запрос на архивацию.
- Ожидаемый результат: 424 Failed Dependency.
- Проверка: Тело ответа согласно `#/components/responses/StatusFailedDependencyError`.
#### 24.3.5 Обработка ошибок (Общие)
**Сценарий 5.1: Внутренняя ошибка сервера (500 Internal Server Error)**
- Предусловия: Имитация неожиданного сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 24.4 Особые моменты для тестирования
1. **Идемпотентность**: Повторная архивация одного и того же квиза не приводит к ошибке.
2. **Видимость**: Заархивированный квиз не отображается в списках активных квизов.
3. **Восстановление**: Если предусмотрено восстановление, проверить возможность восстановления заархивированного квиза.
4. **Логирование**: Проверить запись операции архивации в логах.
5. **Мониторинг**: Проверить метрики и алерты по операциям архивации.
### 24.5 Критерии приемки
1. Все тестовые сценарии из раздела 24.3 пройдены успешно.
2. Квиз корректно помечается как заархивированный в базе данных.
3. Обработка всех кодов ошибок (200, 400, 401, 424, 500) соответствует спецификации API.
4. Операция архивации идемпотентна.
5. Логирование и мониторинг операций архивации работают корректно.
## 25. POST /quiz/move
### 25.1 Описание
Перенос квиза на другой аккаунт. Позволяет изменить владельца квиза, указав идентификатор квиза (`qid`) и новый `accountID`.
### 25.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Тело запроса должно соответствовать схеме `#/components/schemas/QuizMoveRequest` и содержать обязательные поля: `qid` (string), `accountID` (string).
- В случае успеха (200 OK) возвращает подтверждение переноса.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 500 (Internal Server Error).
### 25.3 Тестовые сценарии
#### 25.3.1 Успешный перенос квиза
**Предусловия:**
- Пользователь авторизован.
- Квиз с указанным `qid` существует и принадлежит текущему пользователю.
- Новый `accountID` существует.
**Входные данные:**
- Метод: POST
- URL: /quiz/move
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{ "qid": "uuid-quiz-101", "accountID": "user-2" }
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Квиз теперь принадлежит новому аккаунту (`accountID`).
**Проверки:**
1. В базе данных у квиза обновлён `accountID`.
2. Квиз больше не доступен старому владельцу (если нет прав).
3. Новый владелец видит квиз в списке своих квизов.
#### 25.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 25.3.3 Проверка валидации данных запроса (400 Bad Request)
**Сценарий 3.1: Отсутствие обязательных полей**
- Тело запроса: `{}` или без одного из полей.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на отсутствие обязательных полей.
**Сценарий 3.2: Несуществующий квиз или аккаунт**
- Тело запроса: несуществующий `qid` или `accountID`.
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на несуществующий квиз или аккаунт.
#### 25.3.4 Обработка ошибок (500 Internal Server Error)
**Сценарий 4.1: Внутренняя ошибка сервера**
- Имитация сбоя.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 25.4 Особые моменты для тестирования
1. Проверить, что перенос невозможен без прав на квиз.
2. Проверить, что все связанные сущности (например, результаты) корректно отображаются новому владельцу.
3. Проверить, что квиз не дублируется, а именно переносится.
4. Проверить корректность передачи всех обязательных полей (`qid`, `accountID`).
### 25.5 Критерии приемки
1. Все тестовые сценарии из раздела 25.3 пройдены успешно.
2. Квиз корректно переносится новому владельцу.
3. Обработка всех кодов ошибок (200, 400, 401, 500) соответствует спецификации API.
4. Логирование и мониторинг операций переноса работают корректно.
## 26. POST /quiz/template
### 26.1 Описание
Создание копии шаблона квиза. Возвращает новый идентификатор шаблона.
### 26.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- В случае успеха (200 OK) возвращает объект с новым `id` шаблона.
- Поддерживает HTTP коды ответа: 200 (OK), 401 (Unauthorized), 500 (Internal Server Error).
### 26.3 Тестовые сценарии
#### 26.3.1 Успешное копирование шаблона
**Предусловия:**
- Пользователь авторизован.
- Существует шаблон для копирования.
**Входные данные:**
- Метод: POST
- URL: /quiz/template
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{ "id": 12345 }
```
**Проверки:**
1. В базе данных создан новый шаблон с уникальным `id`.
2. Новый шаблон доступен пользователю.
3. Оригинальный шаблон не изменён.
#### 26.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 26.3.3 Обработка ошибок (500 Internal Server Error)
**Сценарий 3.1: Внутренняя ошибка сервера**
- Имитация сбоя.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 26.4 Особые моменты для тестирования
1. Проверить, что копия шаблона создаётся с новым идентификатором.
2. Проверить, что оригинальный шаблон не изменяется.
3. Проверить корректность структуры ответа (`id` — integer).
### 26.5 Критерии приемки
1. Все тестовые сценарии из раздела 26.3 пройдены успешно.
2. Новый шаблон корректно создаётся и доступен пользователю.
3. Оригинальный шаблон не изменяется.
4. Обработка всех кодов ошибок (200, 401, 500) соответствует спецификации API.
5. Логирование и мониторинг операций копирования работают корректно.
## 27. POST /results/getResults/{quizId}
### 27.1 Описание
Получение результатов по конкретному квизу. Позволяет получить список результатов (ответов) для указанного квиза с поддержкой фильтрации и пагинации.
### 27.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Параметр пути: `quizId` (string, обязательный) — идентификатор квиза.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetResultsRequest` (опциональные поля: `To`, `From`, `New`, `Page`, `Limit`).
- В случае успеха (200 OK) возвращает объект по схеме `#/components/schemas/GetResultsResponse` (поля: `total_count`, `results` — массив объектов `AnswerExport`).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 500 (Internal Server Error).
### 27.3 Тестовые сценарии
#### 27.3.1 Успешное получение результатов квиза
**Предусловия:**
- Пользователь авторизован.
- Квиз с указанным `quizId` существует и содержит результаты.
**Входные данные:**
- Метод: POST
- URL: /results/getResults/{quizId}
- Path parameter: quizId = "12345"
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{ "Page": 1, "Limit": 10 }
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{
"total_count": 100,
"results": [
{
"content": "{...}",
"id": 1,
"new": false,
"created_at": "2024-06-01T12:00:00Z",
"Version": 1
}
// ... другие объекты AnswerExport ...
]
}
```
**Проверки:**
1. Количество элементов в массиве `results` не превышает значение `Limit` из запроса.
2. Все объекты в массиве `results` соответствуют схеме `AnswerExport` (поля: `content`, `id`, `new`, `created_at`, `Version`).
3. Значение `total_count` соответствует общему количеству результатов для данного квиза.
4. Если указаны фильтры (`From`, `To`, `New`), возвращаются только соответствующие результаты.
5. Пагинация работает корректно: при изменении `Page` возвращаются разные срезы данных.
#### 27.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 27.3.3 Проверка валидации данных запроса (400 Bad Request)
**Сценарий 3.1: Некорректный формат параметра `quizId`**
- Path parameter: quizId = "not_a_number"
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный формат идентификатора.
**Сценарий 3.2: Некорректные значения в теле запроса**
- Тело запроса: поля с неверными типами (например, `Page` = "one").
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный тип данных.
**Сценарий 3.3: Несуществующий квиз**
- Path parameter: quizId = "99999"
- Ожидаемый результат: 200 OK и пустой массив `results` (или 400/404, если реализовано иначе).
- Проверка: Если 200 OK — массив `results` пустой, если 400/404 — сообщение указывает, что квиз не найден.
#### 27.3.4 Обработка ошибок (500 Internal Server Error)
**Сценарий 4.1: Внутренняя ошибка сервера**
- Имитация сбоя.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 27.4 Особые моменты для тестирования
1. Проверить корректность работы пагинации (`Page`, `Limit`).
2. Проверить фильтрацию по датам (`From`, `To`) и по признаку `New`.
3. Проверить, что все объекты в массиве `results` соответствуют схеме `AnswerExport`.
4. Проверить корректность значения `total_count`.
5. Проверить работу при большом количестве результатов (нагрузочное тестирование).
### 27.5 Критерии приемки
1. Все тестовые сценарии из раздела 27.3 пройдены успешно.
2. Результаты корректно возвращаются и соответствуют фильтрам и пагинации.
3. Обработка всех кодов ошибок (200, 400, 401, 500) соответствует спецификации API.
4. Логирование и мониторинг операций получения результатов работают корректно.
## 28. DELETE /results/{resultId}
### 28.1 Описание
Удаление (деактивация) конкретного результата (ответа) по его идентификатору. Операция требует авторизации и принимает идентификатор результата в path-параметре.
### 28.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: DELETE.
- Path-параметр: `resultId` (string, обязательный) — идентификатор результата.
- В случае успеха (200 OK) возвращает подтверждение удаления.
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 500 (Internal Server Error).
### 28.3 Тестовые сценарии
#### 28.3.1 Успешное удаление результата
**Предусловия:**
- Пользователь авторизован.
- Результат с указанным `resultId` существует и принадлежит пользователю (или у пользователя есть права на удаление).
**Входные данные:**
- Метод: DELETE
- URL: /results/{resultId}
- Path parameter: resultId = "123456"
- Headers:
- Authorization: Bearer {valid_jwt_token}
**Ожидаемый результат:**
- HTTP Status: 200 OK
- (Тело ответа может быть пустым или содержать подтверждение удаления)
**Проверки:**
1. Результат с указанным `resultId` помечен как удалённый или физически удалён из базы данных.
2. Повторный запрос на удаление этого же результата возвращает 200 OK (операция идемпотентна) или 400/404 (если реализовано иначе).
3. Удалённый результат больше не отображается в списках результатов (например, в /results/getResults/{quizId}).
4. Связанные данные (например, статистика) обновляются корректно (если требуется).
#### 28.3.2 Проверка авторизации
**Сценарий 2.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 2.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 28.3.3 Проверка валидации данных запроса (400 Bad Request)
**Сценарий 3.1: Некорректный формат параметра `resultId`**
- Path parameter: resultId = "not_a_number"
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение об ошибке указывает на неверный формат идентификатора.
**Сценарий 3.2: Несуществующий результат**
- Path parameter: resultId = "99999999"
- Ожидаемый результат: 200 OK (или 400/404, если реализовано иначе).
- Проверка: Если 200 OK — операция идемпотентна, если 400/404 — сообщение указывает, что результат не найден.
#### 28.3.4 Проверка безопасности
**Сценарий 4.1: Попытка удаления чужого результата**
- Предусловие: Пользователь пытается удалить результат, который ему не принадлежит и на который нет прав.
- Ожидаемый результат: 401 Unauthorized или 403 Forbidden (если реализовано).
- Проверка: Операция отклонена, результат не удалён.
#### 28.3.5 Граничные случаи и устойчивость
**Сценарий 5.1: Массовое удаление**
- Последовательное удаление большого количества результатов.
- Ожидаемый результат: Все результаты корректно удаляются, система устойчива к нагрузке.
**Сценарий 5.2: Удаление во время активных операций**
- Попытка удалить результат, который используется в отчёте или статистике.
- Ожидаемый результат: Корректная обработка конкурентных запросов, отсутствие ошибок согласованности.
#### 28.3.6 Обработка ошибок (500 Internal Server Error)
**Сценарий 6.1: Внутренняя ошибка сервера**
- Имитация сбоя.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 28.4 Особые моменты для тестирования
1. Проверить идемпотентность операции удаления.
2. Проверить корректность прав доступа (нельзя удалить чужой результат).
3. Проверить корректность обновления связанных данных (статистика, отчёты).
4. Проверить устойчивость к массовым и конкурентным операциям удаления.
5. Проверить логирование и аудит операций удаления.
6. Проверить мониторинг и алерты по ошибкам удаления.
### 28.5 Критерии приемки
1. Все тестовые сценарии из раздела 28.3 пройдены успешно.
2. Результаты корректно удаляются и не отображаются в списках.
3. Обработка всех кодов ошибок (200, 400, 401, 500) соответствует спецификации API.
4. Операция удаления идемпотентна и безопасна.
5. Логирование и мониторинг операций удаления работают корректно.
## 29. PATCH /result/seen
### 29.1 Описание
Массовое обновление статуса (например, "просмотрено") для набора результатов (ответов) по их идентификаторам. Используется для отметки нескольких результатов как просмотренных или изменённых одним запросом. Требует авторизации.
### 29.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: PATCH.
- Тело запроса должно соответствовать схеме `#/components/schemas/UpdateResultsStatusRequest` и содержать обязательное поле `Answers` — массив идентификаторов результатов (integer, int64).
- В случае успеха (200 OK) статус указанных результатов обновляется (например, поле `new` становится false).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 406 (Not Acceptable, если часть результатов недоступна по правам), 500 (Internal Server Error).
### 29.3 Тестовые сценарии
#### 29.3.1 Успешное массовое обновление статуса результатов
**Предусловия:**
- Пользователь авторизован.
- JWT токен валиден.
- Все результаты с переданными id существуют и доступны пользователю для изменения.
**Входные данные:**
- Метод: PATCH
- URL: /result/seen
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{
"Answers": [101, 102, 103]
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Тело ответа: (может быть пустым или содержать информацию об успешном обновлении)
- В базе данных у всех указанных результатов обновлён статус (например, поле `new` стало false).
**Проверки:**
1. Все указанные id действительно обновлены.
2. Повторный запрос с теми же id не вызывает ошибку (идемпотентность).
3. В истории действий пользователя фиксируется операция.
#### 29.3.2 Частичное отсутствие прав на некоторые результаты (406 Not Acceptable)
**Предусловия:**
- Пользователь авторизован.
- В массиве `Answers` есть id, к которым у пользователя нет доступа.
**Входные данные:**
```json
{
"Answers": [201, 202, 9999]
}
```
**Ожидаемый результат:**
- HTTP Status: 406 Not Acceptable
- Тело ответа: сообщение о невозможности обновить часть результатов из-за недостаточных прав.
**Проверки:**
1. Только доступные результаты обновлены.
2. В журнале безопасности фиксируется попытка доступа к чужим данным.
#### 29.3.3 Переданы несуществующие id результатов (400 Bad Request)
**Предусловия:**
- Пользователь авторизован.
- В массиве `Answers` есть несуществующие id.
**Входные данные:**
```json
{
"Answers": [301, 302, 999999]
}
```
**Ожидаемый результат:**
- HTTP Status: 400 Bad Request
- Тело ответа: сообщение об ошибке ("Некоторые id не найдены").
**Проверки:**
1. Существующие результаты не изменяются.
2. В журнале ошибок фиксируется попытка обновления несуществующих результатов.
#### 29.3.4 Проверка авторизации (401 Unauthorized)
**Сценарий 4.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 4.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 29.3.5 Проверка валидации данных запроса (400 Bad Request)
**Сценарий 5.1: Пустой массив Answers**
- Входные данные:
```json
{ "Answers": [] }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение — "Список id не может быть пустым".
**Сценарий 5.2: Отсутствует поле Answers**
- Входные данные:
```json
{ "ids": [501, 502] }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение — "Некорректный формат запроса".
**Сценарий 5.3: Некорректный тип данных в массиве Answers**
- Входные данные:
```json
{ "Answers": ["not_an_integer", 123] }
```
- Ожидаемый результат: 400 Bad Request.
- Проверка: Сообщение — "Некорректный тип данных".
#### 29.3.6 Обработка ошибок (500 Internal Server Error)
**Сценарий 6.1: Внутренняя ошибка сервера**
- Предусловия: Имитация сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 29.4 Особые моменты для тестирования
1. **Идемпотентность**: Повторный PATCH с теми же id не должен приводить к ошибке.
2. **Массовое обновление**: Проверить работу с большим количеством id (например, 1000 за раз).
3. **Корректность логирования и аудита**: Все операции фиксируются.
4. **Права доступа**: Нельзя обновить результаты, принадлежащие другому аккаунту.
5. **Валидация**: Проверить все граничные случаи для массива Answers (пустой, отсутствует, неверный тип).
### 29.5 Критерии приемки
1. Все тестовые сценарии из раздела 29.3 пройдены успешно.
2. Статусы результатов обновляются корректно.
3. Обработка всех кодов ошибок (200, 400, 401, 406, 500) соответствует спецификации API.
4. Операция идемпотентна и безопасна.
5. Логирование и аудит операций обновления работают корректно.
## 30. POST /results/{quizID}/export
### 30.1 Описание
Экспорт результатов квиза в формате Excel (XLSX) по идентификатору квиза. Позволяет выгрузить ответы пользователей с возможностью фильтрации по дате, новизне, пагинации. Требует авторизации.
### 30.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST.
- Параметр пути: `quizID` (string) — идентификатор квиза, результаты которого экспортируются.
- Тело запроса должно соответствовать схеме `#/components/schemas/GetResultsRequest` и может содержать поля:
- `To` (string, date-time) — верхняя граница даты.
- `From` (string, date-time) — нижняя граница даты.
- `New` (boolean) — фильтр только по новым результатам.
- `Page` (integer, uint64) — номер страницы.
- `Limit` (integer, uint64) — количество результатов на странице.
- В случае успеха (200 OK) возвращает файл Excel (MIME: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`).
- Поддерживает HTTP коды ответа: 200 (OK), 400 (Bad Request), 401 (Unauthorized), 402 (Payment Required), 500 (Internal Server Error).
### 30.3 Тестовые сценарии
#### 30.3.1 Успешный экспорт результатов квиза
**Предусловия:**
- Пользователь авторизован.
- Квиз с указанным `quizID` существует и доступен пользователю.
- В базе есть результаты для экспорта.
**Входные данные:**
- Метод: POST
- URL: /results/{quizID}/export (например, /results/123/export)
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{
"From": "2023-01-01T00:00:00Z",
"To": "2023-01-31T23:59:59Z",
"New": false,
"Page": 1,
"Limit": 100
}
```
**Ожидаемый результат:**
- HTTP Status: 200 OK
- Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- В ответе — бинарный файл Excel с выгруженными результатами (структура и количество строк соответствуют фильтрам).
**Проверки:**
1. Файл корректно открывается в Excel.
2. Количество строк соответствует ожидаемому количеству результатов.
3. Данные в файле соответствуют фильтрам (даты, новизна, пагинация).
4. Названия столбцов и формат данных соответствуют спецификации.
#### 30.3.2 Экспорт с фильтрацией по новым результатам
**Входные данные:**
```json
{
"New": true
}
```
- Ожидаемый результат: В файле только новые результаты.
#### 30.3.3 Экспорт с пагинацией
**Входные данные:**
```json
{
"Page": 2,
"Limit": 50
}
```
- Ожидаемый результат: В файле только результаты второй страницы (51-100).
#### 30.3.4 Экспорт с фильтрацией по диапазону дат
**Входные данные:**
```json
{
"From": "2023-02-01T00:00:00Z",
"To": "2023-02-28T23:59:59Z"
}
```
- Ожидаемый результат: В файле только результаты, попавшие в указанный диапазон дат.
#### 30.3.5 Проверка авторизации (401 Unauthorized)
**Сценарий 5.1: Отсутствие токена**
- Headers: без Authorization.
- Ожидаемый результат: 401 Unauthorized.
- Проверка: Тело ответа согласно `#/components/responses/UnauthorizedError`.
**Сценарий 5.2: Невалидный токен**
- Headers: Authorization: Bearer invalid_jwt_token.
- Ожидаемый результат: 401 Unauthorized.
#### 30.3.6 Проверка оплаты (402 Payment Required)
- Предусловие: У пользователя нет необходимой подписки/прав для экспорта.
- Ожидаемый результат: 402 Payment Required.
#### 30.3.7 Проверка валидации данных запроса (400 Bad Request)
**Сценарий 7.1: Некорректный формат даты**
- Входные данные:
```json
{ "From": "not-a-date" }
```
- Ожидаемый результат: 400 Bad Request.
**Сценарий 7.2: Некорректный тип данных**
- Входные данные:
```json
{ "Page": "one", "Limit": "ten" }
```
- Ожидаемый результат: 400 Bad Request.
**Сценарий 7.3: Несуществующий quizID**
- URL: /results/999999/export
- Ожидаемый результат: 400 Bad Request или пустой файл (уточнить по реализации).
#### 30.3.8 Обработка ошибок (500 Internal Server Error)
- Предусловие: Имитация сбоя на сервере.
- Ожидаемый результат: 500 Internal Server Error.
- Проверка: Тело ответа согласно `#/components/responses/InternalServerError`.
### 30.4 Особые моменты для тестирования
1. **Корректность структуры файла**: Проверить, что все ожидаемые столбцы присутствуют, формат данных соответствует требованиям.
2. **Фильтрация и пагинация**: Проверить, что фильтры и пагинация работают корректно и не влияют на целостность данных.
3. **Большой объём данных**: Проверить экспорт при большом количестве результатов (например, 10 000+ строк).
4. **Права доступа**: Проверить, что нельзя экспортировать результаты чужого квиза.
5. **Логирование и аудит**: Проверить, что операция экспорта фиксируется в логах.
### 30.5 Критерии приемки
1. Все тестовые сценарии из раздела 30.3 пройдены успешно.
2. Файл Excel формируется корректно, данные соответствуют фильтрам и структуре.
3. Обработка всех кодов ошибок (200, 400, 401, 402, 500) соответствует спецификации API.
4. Операция экспорта безопасна и не влияет на целостность данных.
5. Логирование и аудит операций экспорта работают корректно
## 31. GET /result/{resultID}
31.1 Описание
Получение списка ответов (Answer) для указанного resultID. Ответы включают информацию о выбранных пользователем вариантах, устройстве, сессии и мета-данных (например, UTM-метки).
31.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: GET.
- Параметр resultID передается в path и является обязательным (string).
- В случае успеха (200 OK) возвращает массив объектов Answer.
- Поддерживает HTTP коды ответа:
- 200 OK — Успешное получение данных.
- 400 Bad Request — Некорректный resultID.
- 401 Unauthorized — Отсутствие или невалидный токен.
- 402 Payment Required — Требуется оплата (например, при превышении квоты).
- 500 Internal Server Error — Внутренняя ошибка сервера.
31.3 Тестовые сценарии
**31.3.1 Успешное получение списка ответов по resultID**
_Предусловия:_
- Пользователь авторизован.
- resultID существует и соответствует завершенному прохождению квиза.
- В базе данных имеются связанные ответы.
_Входные данные:_
- Метод: GET
- URL: /result/abc123xyz
- Headers:
- Authorization: Bearer {valid_jwt_token}
_Ожидаемый результат:_
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
[
{
"Id": 98765,
"content": "{\"selected\": [1, 2]}",
"question_id": 321,
"QuizId": 101,
"Fingerprint": "device-fp-001",
"Session": "session-uuid-001",
"Result": true,
"CreatedAt": "2024-12-01T12:00:00Z",
"new": false,
"Deleted": false,
"Email": "user@example.com",
"DeviceType": "mobile",
"Device": "iPhone 13",
"Browser": "Safari",
"IP": "192.168.1.10",
"OS": "iOS 17",
"Start": false,
"Utm": {
"utm_source": "newsletter",
"utm_campaign": "winter_sale"
},
"Version": 3
}
]
```
_Проверки:_
- Ответ — JSON-массив.
- Каждый элемент соответствует объекту Answer по схеме.
- Указан корректный resultID, и все ответы относятся к нему.
- Присутствуют все поля, включая Session, Device, IP, content, Utm и др.
- В поле content корректно сериализован JSON, соответствующий типу вопроса.
- Поля Deleted, Start, Result, new — логические, отражают статус ответа.
- Если Utm присутствует, он представлен как объект с произвольными ключами и строковыми значениями.
- Убедиться в правильности формата поля CreatedAt (RFC 3339).
**31.3.2 Проверка авторизации**
_Сценарий 2.1: Отсутствие токена_
- Headers: отсутствует Authorization
- Ожидаемый результат: 401 Unauthorized
_Сценарий 2.2: Невалидный токен_
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
**31.3.3 Проверка обработки ошибок (400, 402, 500)**
_Сценарий 3.1: Некорректный resultID (например, пустая строка или недопустимый UUID)_
- URL: /result/
- Ожидаемый результат: 400 Bad Request
_Сценарий 3.2: resultID не существует_
- URL: /result/nonexistent123
- Ожидаемый результат: 200 OK, но пустой массив []
_Сценарий 3.3: Требуется оплата_
- Предусловия: Квота использования исчерпана или аккаунт не активен.
- Ожидаемый результат: 402 Payment Required
_Сценарий 3.4: Внутренняя ошибка сервера_
- Предусловия: Имитация сбоя на уровне БД
- Ожидаемый результат: 500 Internal Server Error
31.4 Особые моменты для тестирования
- Структура Answer: Проверить наличие всех полей, особенно content, Utm, Email, DeviceType, Browser.
- Массив ответов может быть пустым: Проверить поведение при отсутствии ответов.
- Согласованность данных: Все ответы должны относиться к одному resultID.
- UTM-метки: Должны корректно отображаться и соответствовать параметрам в момент начала прохождения квиза.
- Безопасность: Нельзя получить данные по resultID, если токен не авторизован.
- Поля с типом boolean: Проверить логическую корректность (Result, Start, Deleted, new).
- Формат времени: CreatedAt должен быть корректно сериализован в формате date-time.
31.5 Критерии приемки
- Эндпоинт возвращает ответы, соответствующие resultID.
- Все поля объекта Answer соответствуют описанию схемы.
- Работает авторизация: без токена — 401, с невалидным токеном — 401.
- Ошибки при недопустимом resultID возвращают 400, при проблемах оплаты — 402.
- При пустом результате возвращается пустой JSON-массив.
- Внутренние сбои сервера корректно обрабатываются с кодом 500.
- UTM-данные корректно отображаются, если присутствуют.
- Проверка структуры и типов данных пройдена.
- Все поля имеют предсказуемую структуру, включая вложенные объекты.
## 32. POST /statistic/{quizID}/devices
32.1 Описание
Получение статистики по устройствам, операционным системам и браузерам, использованным участниками при прохождении квиза с указанным quizID. Статистика рассчитывается на основе ответов, у которых res = true (итоговые ответы).
32.2 Базовые требования
- Требуется JWT токен авторизации (bearerAuth).
- Метод запроса: POST
- Путь должен содержать обязательный параметр quizID (string).
- Тело запроса должно соответствовать схеме DeviceStatRequest:
- From — начало временного диапазона (timestamp, опционально)
- To — конец временного диапазона (timestamp, опционально)
- В случае успеха (200 OK) возвращается объект, содержащий три словаря с процентным распределением:
- Device, OS, Browser
- Поддерживаемые коды ответа:
- 200 OK
- 400 Bad Request (например, неверный формат дат или параметров)
- 401 Unauthorized (отсутствие или невалидный токен)
- 500 Internal Server Error (непредвиденная ошибка на сервере)
32.3 Тестовые сценарии
**32.3.1 Успешное получение статистики по устройствам**
_Предусловия:_
- Пользователь авторизован.
- quizID существует.
- В базе есть ответы с res=true в указанном диапазоне дат или без фильтрации по времени.
_Входные данные:_
- Метод: POST
- URL: /statistic/12345/devices
- Headers:
- Authorization: Bearer {valid_jwt_token}
- Content-Type: application/json
- Тело запроса:
```json
{
"From": 1700000000,
"To": 1709999999
}
```
_Ожидаемый результат:_
- HTTP Status: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{
"devices": [
{
"Device": {
"mobile": 65.5,
"desktop": 30.2,
"tablet": 4.3
},
"OS": {
"iOS": 40.1,
"Android": 25.4,
"Windows": 20.0,
"macOS": 14.5
},
"Browser": {
"Chrome": 60.0,
"Safari": 25.0,
"Firefox": 10.0,
"Edge": 5.0
}
}
]
}
```
_Проверки:_
- Код ответа — 200 OK.
- Поле devices — массив длиной 1, содержащий объект.
- Все вложенные объекты Device, OS, Browser представлены как словари (object) с ключами-строками и значениями с типом float.
- Все значения — проценты (0–100), в сумме могут (но не обязаны) давать 100 по каждому разделу.
- Устройства, ОС и браузеры перечислены корректно, названия в формате строк (например, "iOS", "Chrome").
- При наличии фильтра From и To, данные учитываются только в этом диапазоне.
- Если ответов с res=true нет, вернётся пустые объекты или объекты с нулями:
```json
{
"devices": [
{
"Device": {},
"OS": {},
"Browser": {}
}
]
}
```
**32.3.2 Без временного диапазона (все данные)**
_Входные данные:_
```json
{}
```
_Ожидаемый результат:_
- HTTP Status: 200 OK
- Возвращается статистика по всем ответам res=true без фильтрации по времени.
**32.3.3 Проверка авторизации**
_Сценарий 3.1: Отсутствие токена_
- Headers: без Authorization
- Ожидаемый результат: 401 Unauthorized
_Сценарий 3.2: Невалидный токен_
- Headers: Authorization: Bearer invalid_token
- Ожидаемый результат: 401 Unauthorized
**32.3.4 Проверка валидации (400 Bad Request)**
_Сценарий 4.1: Некорректный формат timestamp_
```json
{
"From": "not_a_timestamp",
"To": 1700000000
}
```
_Сценарий 4.2: quizID некорректного формата_
- URL: /statistic/!!invalid/devices
_Ожидаемый результат (для обоих):_
- HTTP Status: 400 Bad Request
- Сообщение об ошибке указывает на некорректный формат входных данных.
**32.3.5 Внутренняя ошибка сервера**
_Сценарий 5.1: Ошибка чтения из базы_
- Эмуляция сбоя на уровне БД.
_Ожидаемый результат:_
- HTTP Status: 500 Internal Server Error
32.4 Особые моменты для тестирования
- Корректность расчетов: Проверить, что доли рассчитаны правильно (например, из 100 ответов: 60 мобильных → 60%).
- Диапазон значений: Убедиться, что значения в процентах, тип float, в пределах 0–100.
- Сумма значений: Проверить, что сумма по каждой категории не превышает 100 (но допускается < 100 из-за округления или пропущенных значений).
- Поведение при отсутствии данных: Убедиться, что возвращается пустая статистика, а не ошибка.
- Большие объемы данных: Проверить производительность и корректность при большом числе ответов.
- Безопасность: Нельзя получить статистику без авторизации.
- Точность по времени: Проверить корректную фильтрацию по From и To, включая граничные значения.
32.5 Критерии приемки
- Все тестовые сценарии из раздела 32.3 выполняются успешно.
- Результаты статистики корректны по значениям и структуре.
- Поля Device, OS, Browser соответствуют требованиям по типам и форматам.
- Диапазон дат фильтрует данные правильно.
- Ошибки в запросах возвращают 400, отсутствие авторизации — 401.
- Сервер обрабатывает сбои корректно с кодом 500.
## 33. POST /statistic/{quizID}/general
33.1 Описание
Получение общей статистики по квизу с указанным quizID, включая:
- количество открытий (Open)
- количество завершений (Result)
- среднее время прохождения (AvTime)
- конверсию (Conversion)
Все значения сгруппированы по дате/временным меткам. Поддерживается фильтрация по диапазону времени с помощью полей From и To.
33.2 Базовые требования
- Метод: POST
- Аутентификация: bearerAuth (JWT токен обязателен)
- Параметры пути:
- quizID — идентификатор квиза (обязателен, string)
- Тело запроса: JSON по схеме DeviceStatRequest:
- From — начало диапазона (timestamp)
- To — конец диапазона (timestamp)
- Код ответа 200 OK содержит объект с полями:
- Open, Result, AvTime, Conversion — объекты вида { timestamp: value }
33.3 Тестовые сценарии
**33.3.1 Успешное получение общей статистики**
_Предусловия:_
- Пользователь авторизован
- Существуют записи статистики для указанного quizID
_Входные данные:_
- URL: /statistic/12345/general
- Headers:
- Authorization: Bearer {valid_token}
- Content-Type: application/json
- Тело:
```json
{
"From": 1700000000,
"To": 1709999999
}
```
_Ожидаемый результат:_
- Код: 200 OK
- Content-Type: application/json
- Тело ответа:
```json
{
"Open": {
"1700010000": 150,
"1700020000": 175
},
"Result": {
"1700010000": 80,
"1700020000": 90
},
"AvTime": {
"1700010000": 45000,
"1700020000": 48000
},
"Conversion": {
"1700010000": 53,
"1700020000": 51
}
}
```
_Проверки:_
- Код — 200 OK
- Все поля (Open, Result, AvTime, Conversion) — объекты (object)
- Ключи — строки, представляющие timestamp (можно дополнительно валидировать диапазон)
- Значения:
- Open и Result — целые числа (uint64)
- AvTime — среднее время в миллисекундах (uint64)
- Conversion — проценты (0–100), округленные целые числа (uint64)
- Даты фильтруются по диапазону From–To
**33.3.2 Без временного диапазона (все данные)**
_Вход:_
```json
{}
```
_Ожидаемый результат:_
- Код: 200 OK
- Данные по всей доступной статистике
**33.3.3 Нет данных в указанном диапазоне**
_Вход:_
```json
{
"From": 1500000000,
"To": 1500003600
}
```
_Ожидаемый результат:_
```json
{
"Open": {},
"Result": {},
"AvTime": {},
"Conversion": {}
}
```
**33.3.4 Ошибки авторизации**
_Сценарий 1: Без токена_
- Headers: отсутствует Authorization
- Ответ: 401 Unauthorized
_Сценарий 2: Невалидный токен_
- Headers: Authorization: Bearer invalid_token
- Ответ: 401 Unauthorized
**33.3.5 Ошибка валидации (400 Bad Request)**
_Примеры:_
- From или To не число:
```json
{
"From": "invalid",
"To": 1700000000
}
```
- quizID содержит запрещённые символы:
/statistic/!!bad_id/general
_Ожидаемый результат:_
- Код: 400 Bad Request
**33.3.6 Внутренняя ошибка сервера**
_Например:_
- Ошибка доступа к базе
- Исключение во время обработки статистики
_Ожидаемый результат:_
- Код: 500 Internal Server Error
33.4 Особенности и проверки
| Поле | Описание | Проверки |
|-----------|-----------------------------------------------|--------------------------------------------------------|
| Open | Количество открытий квиза по timestamp'ам | timestamp: uint64 |
| Result | Кол-во завершений (ответов с res=true) | timestamp: uint64 |
| AvTime | Среднее время прохождения (в мс) | Значения > 0, логично по отношению к Result |
| Conversion| Конверсия: Result / Open * 100 округлённое | От 0 до 100, должно коррелировать с Open и Result |
33.5 Критерии приёмки
- Авторизация обязательна
- Корректный диапазон по времени работает
- Формат timestamp и значений верный
- Конверсия адекватна (Open > 0)
- Ошибки корректно обрабатываются (400, 401, 500)
- Пустая статистика не вызывает ошибок
- Статистика агрегируется по временным ключам (по дню/часу, в зависимости от реализации)
34. POST /statistic/{quizID}/questions
34.1 Описание
Запрос возвращает подробную статистику по вопросам квиза, включая:
- Метрики воронки (Funnel)
- Подробные данные воронки (FunnelData)
- Процент завершений по каждому вопросу (Results)
- Распределение ответов по каждому вопросу (Questions)
Фильтрация осуществляется по диапазону времени (From – To), и применяется только к ответам с Result=true.
34.2 Параметры
- Метод: POST
- Аутентификация: bearerAuth
- Путь: /statistic/{quizID}/questions
- quizID — string (обязателен)
- Тело запроса: JSON по схеме DeviceStatRequest
```json
{
"From": 1700000000,
"To": 1709999999
}
```
34.3 Описание структуры ответа (QuestionStat)
```json
{
"Funnel": [95.5, 78.3, 60.1],
"FunnelData": [1200, 900, 750, 600],
"Results": {
"Как вы оцениваете наш сервис?": 87.5,
"Сколько вам лет?": 92.0
},
"Questions": {
"Как вы оцениваете наш сервис?": {
"Отлично": 65.2,
"Хорошо": 25.3,
"Удовлетворительно": 9.5
},
"Сколько вам лет?": {
"18-24": 40.0,
"25-34": 30.0,
"35-44": 20.0,
"45+": 10.0
}
}
}
```
| Поле | Тип | Описание |
|-----------|----------|-------------------------------------------------------|
| Funnel | float[] | Метрики воронки (максимум 3 значения) — в процентах |
| FunnelData| float[] | Дополнительные данные воронки (максимум 4 знач.) |
| Results | object | Карта вопрос: % завершивших |
| Questions | object | Карта вопрос: { ответ: % распределения } |
34.4 Тест-кейсы
**✅ 34.4.1 Успешное получение статистики по вопросам**
_Вход:_
POST /statistic/12345/questions
Authorization: Bearer valid_token
Content-Type: application/json
Тело:
```json
{
"From": 1700000000,
"To": 1709999999
}
```
_Ожидаемый ответ:_
- Код: 200 OK
- Тип: application/json
- Структура: массив объектов QuestionStat (в большинстве случаев массив из 1 элемента)
_Проверки:_
- Funnel.length <= 3, FunnelData.length <= 4
- Значения — float
- Results: ключи — строки, значения — float (0.0 – 100.0)
- Questions: ключи — строки (вопросы), значения — объекты с ответ: % (в пределах 0.0–100.0)
- Сумма распределения по каждому вопросу ≈ 100 (можно ±0.1)
**✅ 34.4.2 Пустая статистика по диапазону**
_Тело запроса:_
```json
{
"From": 1500000000,
"To": 1500003600
}
```
_Ожидаемый результат:_
```json
[
{
"Funnel": [],
"FunnelData": [],
"Results": {},
"Questions": {}
}
]
```
**🔒 34.4.3 Ошибка авторизации**
_Сценарии:_
- Без токена → 401 Unauthorized
- С невалидным токеном → 401 Unauthorized
**⚠️ 34.4.4 Ошибка валидации запроса**
_Примеры:_
- From и To не числа:
```json
{
"From": "abc",
"To": 123
}
```
- quizID некорректен:
/statistic/<>/questions
_Ожидаемый результат:_ 400 Bad Request
**💥 34.4.5 Внутренняя ошибка**
_Примеры:_
- Ошибка агрегации данных
- Сбой базы
_Ожидаемый результат:_ 500 Internal Server Error
34.5 Критерии приёмки
| Условие | Проверка |
|-------------------------------------------|----------|
| ✅ Авторизация обязательна | Да |
| ✅ Статистика корректно фильтруется по времени | Да |
| ✅ Пустые данные не вызывают ошибок | Да |
| ✅ Значения — в допустимых диапазонах | Да |
| ✅ Все поля валидируются | Да |