Задание
Протестируйте API по этим требованиям и по описанию ручек в Swagger. Требования — главный источник: если сервис ведёт себя иначе, чем здесь написано, это дефект сервиса. Если требования и описание в Swagger противоречат друг другу или чего-то не договаривают, это дефект документации — его тоже нужно найти и описать.
Что сдать
- чек-лист или тест-кейсы на все ручки, с техниками тест-дизайна: классы эквивалентности, граничные значения, таблица решений для активации;
- баг-репорты на найденные дефекты сервиса, со ссылкой на номер требования (например, R-C3);
- список замечаний к документации;
- коллекцию Postman с проверками в Tests.
Как начать
- Возьмите свой токен в кабинете ученика.
- Откройте вкладку API, нажмите Authorize и вставьте токен.
- В Postman: Authorization → Bearer Token, адрес сервиса —
https://swaggerhub.quickoffer.ru. Спецификацию можно импортировать по ссылкеhttps://swaggerhub.quickoffer.ru/openapi.json.
Купоны и активации живут в вашей песочнице: их видят только запросы с вашим токеном. Данные старше 60 дней удаляются.
Общие правила
- R-E1
Все ручки API требуют заголовок
Authorization: Bearer <токен>.Нет заголовка или токен пустой →
401, reasonAuthorization required: pass your token as Authorization: Bearer <token>. Токен неверный →401, reasonInvalid token. - R-E2
Каждый токен работает в своей песочнице: купоны и активации одного токена недоступны другому.
- R-E3
Тело запроса — JSON-объект с заголовком
Content-Type: application/json.Другой Content-Type →
415. Тело не JSON →400Invalid JSON. Тело не объект (массив, строка) →400. Тело больше 64 КБ →413. - R-E4
Даты — ISO 8601 с часовым поясом:
2026-11-20T10:00:00Zили2026-11-20T13:00:00+03:00. В ответах даты всегда в UTC сZ, с точностью до секунды; дробная часть секунд отбрасывается. - R-E5
«Сейчас» — момент, когда сервер получил запрос.
- R-E6
Неизвестный путь →
404, reasonNot Found. Известный путь с другим методом →405. - R-E7
Сервис не отвечает
500ни на какие входные данные: любой неверный запрос получает понятную ошибку4xx. - R-E8
Проверки выполняются в том порядке, в каком перечислены требования ручки, и возвращается первая найденная ошибка.
Создание купона
POST /coupon/create
Ошибки этой ручки: 400 и тело {"success": false, "reason": "…"}.
- R-C1
Обязательные поля:
title,amount,validUntil. Поле отсутствует или равноnull— ошибка.reason
title is required,amount is required,validUntil is required. - R-C2
title— только заглавные латинские буквы и цифры, длина от 5 до 30 символов включительно.reason
title must match ^[A-Z0-9]{5,30}$. - R-C3
amount— целое число от 1 до 1000 включительно. Дробное число, строка (даже"100"),true/falseне принимаются.reason
amount must be an integer from 1 to 1000. - R-C4
validUntilиvalidSince— даты по R-E4.reason
validUntil must be a date-time in ISO 8601 with time zone(для validSince — так же). - R-C5
validSinceнеобязателен. Если его нет,validSince=createdAt. Дата начала в прошлом допустима, в будущем — тоже: тогда купон ещё не начал действовать. - R-C6
validUntilстрого позжеvalidSince. Равные даты — ошибка.reason
validUntil must be later than validSince. - R-C7
validUntilне может быть в прошлом или равен «сейчас»: купон, который уже истёк, создать нельзя.reason
validUntil must be in the future. - R-C8
titleуникален в песочнице. После удаления купона егоtitleснова свободен.reason
Coupon with this title already exists. - R-C9
Успех —
200и все поляCreateCouponResponse.createdAt— время запроса,idуникален. Новый купон ещё не активирован.
Активация купона
POST /coupon/activate
Ошибки этой ручки: 400 и тело {"success": false, "status": "…", "message": "…"}.
- R-AC1
Тело:
userId— непустая строка до 64 символов,data— объект,data.title— непустая строка.status
error, messageInvalid value for the userId field/…the data field/…the title field. - R-AC2
Купон ищется по
titleточно, с учётом регистра:gift123иGIFT123— разные купоны.Не найден → status
notFound, messageCoupon not found. - R-AC3
Купон одноразовый: если он уже активирован (любым пользователем) и активация не отменена — повторно нельзя.
status
alreadyActivated, messageCoupon already activated. - R-AC4
Раньше
validSinceактивировать нельзя. Ровно в моментvalidSince— уже можно.status
tooEarly, messageCoupon is not active yet. - R-AC5
Позже
validUntilактивировать нельзя. Ровно в моментvalidUntil— ещё можно.status
tooLate, messageCoupon has expired. - R-AC6
Один пользователь (
userId) может активировать не больше 3 купонов за последние 24 часа. Отменённые активации не считаются.4-я активация → status
error, messageDaily activation limit exceeded. - R-AC7
Успех —
200и{"success": true}. Купон получает статусactivated.
Отмена активации
POST /coupon/cancel
- R-X1
Тело — как у активации (R-AC1), ошибки формата — status
error. - R-X2
Купон не найден (с учётом регистра) →
notFound. Купон не активирован →notActivated. Купон активировал другойuserId→notOwner. - R-X3
Отменить можно в течение 15 минут после активации, включительно. Позже →
tooLate. - R-X4
После отмены купон снова свободен: его может активировать любой пользователь, а отменённая активация не входит в лимит R-AC6.
Карточка купона
GET /coupon/{title}
- R-G1
Поиск по
titleс учётом регистра. Не найден →404, reasonCoupon not found. - R-G2
status:activated— есть неотменённая активация; иначеupcoming— «сейчас» раньшеvalidSince,expired— позжеvalidUntil,active— в остальных случаях. - R-G3
activatedByиactivatedAt— данные неотменённой активации, иначеnull.
Список купонов
GET /coupon/list
- R-L1
Сортировка: сначала новые по
createdAt, при равенстве — по убываниюid. - R-L2
status— необязательный фильтр, одно изupcoming,active,expired,activated(как в R-G2). Другое значение →400. - R-L3
limit— целое от 1 до 50, по умолчанию 10.offset— целое от 0, по умолчанию 0. Неверное значение →400. - R-L4
total— сколько купонов подходит под фильтр без учёта пагинации.offsetбольшеtotal—200и пустойitems.
Изменение купона
PATCH /coupon/{title}
- R-P0
Купон ищется по
titleс учётом регистра, это проверяется первым. Не найден →404. - R-P1
Менять можно только
amountиvalidUntil, хотя бы одно из них. Другое поле →400Only amount and validUntil can be changed. Пустой объект →400. - R-P2
Новые значения проверяются так же, как при создании: R-C3, R-C4, R-C6, R-C7.
- R-P3
Активированный купон менять нельзя →
400Activated coupon cannot be changed. - R-P4
Успех —
200и обновлённая карточка купона.
Удаление купона
DELETE /coupon/{title}
- R-D1
Купон не найден →
404. Активированный купон удалить нельзя →400Activated coupon cannot be deleted. Успех —200и{"success": true}.
Расчёт заказа с купоном
POST /order/calculate
- R-O1
Обязательные поля:
userId(строка как в R-AC1),title(непустая строка),orderTotal(целое от 1 до 1 000 000). Иначе → statuserror. - R-O2
Купон не найден →
notFound. Купон не активирован →notActivated. - R-O3
Купон может применить только тот пользователь, который его активировал. Другой
userId→notOwner. - R-O4
Активированный купон действует до
validUntil. Позже →tooLate. - R-O5
Минимальная сумма заказа с купоном — 500. Меньше →
minOrderTotal. - R-O6
total=orderTotal−amount, но не меньше 1.discount=orderTotal−total. Расчёт ничего не сохраняет и купон не тратит: его можно считать сколько угодно раз.
Купоны пользователя
GET /user/{userId}/coupons
- R-U1
Неотменённые активации пользователя, сначала новые. Отменённые не показываются. Пользователь без активаций —
200и пустойitems.