Создание нового вопроса в системе. Эндпоинт позволяет добавлять вопросы к существующим квизам или как независимые сущности, в зависимости от логики приложения и схемы `QuestionCreateRequest`.
"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`).
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
#### 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: Время создания вопроса**
- Проверка: Время ответа на запрос создания вопроса <Nms(например,<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 квиза
// ...могут быть другие вопросы, если 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`).
- Проверка: Тело ответа содержит сообщение об ошибке авторизации.
#### 13.3.3 Проверка валидации данных запроса (Ответ 400 Bad Request)
(Все поля в `GetQuestionListRequest` являются опциональными, если не указано иное в схеме. Проверки ниже касаются корректности форматов и значений, если поля предоставлены.)
- Проверка: Сообщение об ошибке указывает на невалидные параметры фильтрации.
-**Сценарий 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` (если есть ограничения).
-`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 Успешное обновление одного поля вопроса
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`.
- Предусловия: (Определить условия на основе спецификации `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 оригинального вопроса
"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`.
- Ожидаемый результат: 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`):
"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`.
- Ожидаемый результат: 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).
- Тело ответа (согласно `#/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 уже был удален/деактивирован.
#### 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": "Детальный тест на знание столиц и стран.",
"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`), установлены в эти значения по умолчанию, если они не были переданы в запросе.
#### 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` и содержит информативное сообщение.
- Ожидаемый результат: 400/422, либо данные сохранены корректно (экранированы), и XSS не выполняется при отображении этих данных.
- Проверка: Отсутствие уязвимости к XSS.
#### 18.3.7 Проверка производительности
**Сценарий 7.1: Время создания квиза**
- Проверка: Время ответа на запрос создания квиза <Nms(например,<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`).
- Входные данные: `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: Время ответа на получение списка**
- Проверка: Время ответа <Nms(например,<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).
-`from`/`to`: выборка квизов, созданных/измененных в указанном временном диапазоне (проверить по `created_at` или `updated_at` в зависимости от логики).
-`search`: поиск по релевантным текстовым полям квиза (например, `name`, `description`). Проверить чувствительность к регистру, поиск по части слова.
-`status`: выборка квизов только с указанным статусом.
-`deleted`: выборка только удаленных (`true`) или только неудаленных (`false`/отсутствие) квизов.
-`archived`: выборка только архивированных (`true`) или только неархивированных (`false`/отсутствие) квизов.
-`super`: выборка "супер-квизов" (`true`) или обычных (`false`/отсутствие).
* Корректность работы комбинации нескольких фильтров одновременно.
* Поведение при передаче несуществующих значений для фильтров (например, `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 запросе квиза) обновлены в соответствии с переданными в запросе полями.
- Предусловия: (Определить условия на основе спецификации `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: Время обновления квиза**
- Проверка: Время ответа на запрос обновления <Nms(например,<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 - копируется из оригинала
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`.
- Ожидаемый результат: 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`):
"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`.
- Ожидаемый результат: 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`.
- Ожидаемый результат: 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`.
- Ожидаемый результат: 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`.
- Тело запроса должно соответствовать схеме `#/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`.
- Проверка: Сообщение об ошибке указывает на неверный формат идентификатора.
**Сценарий 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-параметре.
- Проверка: Сообщение об ошибке указывает на неверный формат идентификатора.
**Сценарий 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.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)
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.