Refactor petstore tests and refresh build setup
This commit is contained in:
39
.gitignore
vendored
39
.gitignore
vendored
@@ -1,41 +1,6 @@
|
|||||||
target/
|
target/
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
|
||||||
!**/src/main/**/target/
|
|
||||||
!**/src/test/**/target/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
.idea/modules.xml
|
|
||||||
.idea/jarRepositories.xml
|
|
||||||
.idea/compiler.xml
|
|
||||||
.idea/libraries/
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
build/
|
build/
|
||||||
!**/src/main/**/build/
|
.idea/
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
*.iml
|
||||||
### Mac OS ###
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/.idea/
|
|
||||||
/.mvn/
|
|
||||||
/.gitignore
|
|
||||||
|
|||||||
130
README.md
130
README.md
@@ -1,80 +1,64 @@
|
|||||||
# Автотесты для Petstore API
|
# OTUS Rest-assured Homework 3
|
||||||
|
|
||||||
Этот проект содержит набор автотестов для [Swagger Petstore API](https://petstore.swagger.io/v2) с использованием библиотек **Rest-assured** и **JUnit 5**. Проект оформлен как Maven-проект с поддержкой JDK 24.
|
## Цель проекта
|
||||||
|
Автоматизировать API-сценарии для `https://petstore.swagger.io` с использованием Rest-assured, JUnit Jupiter и quality gates (Checkstyle + SpotBugs).
|
||||||
|
|
||||||
|
## Стек технологий
|
||||||
|
- Java 21
|
||||||
|
- Maven
|
||||||
|
- Rest-assured `6.0.0`
|
||||||
|
- JUnit Jupiter `6.0.2`
|
||||||
|
- Jackson Databind `2.21.0`
|
||||||
|
- Commons Codec `1.21.0`
|
||||||
|
- Maven Surefire Plugin `3.5.4`
|
||||||
|
- Maven Compiler Plugin `3.15.0`
|
||||||
|
- Checkstyle (`maven-checkstyle-plugin` `3.6.0`)
|
||||||
|
- SpotBugs (`spotbugs-maven-plugin` `4.9.8.2`)
|
||||||
|
|
||||||
|
## Реализованные сценарии
|
||||||
|
1. `POST /pet` + `GET /pet/{id}`:
|
||||||
|
- создание питомца;
|
||||||
|
- повторное чтение по id;
|
||||||
|
- проверка, что вернулся именно созданный объект.
|
||||||
|
|
||||||
|
2. `GET /pet/{id}`:
|
||||||
|
- проверка схемы ответа через Json Schema Validator.
|
||||||
|
|
||||||
|
3. Негативные сценарии:
|
||||||
|
- `POST /pet` с malformed JSON возвращает ошибку;
|
||||||
|
- `GET /pet/{id}` для удаленного/несуществующего id возвращает `404`;
|
||||||
|
- `GET /pet/findByStatus` с невалидным статусом возвращает пустой список.
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
- 2-уровневый дизайн:
|
||||||
|
- `service` слой (HTTP-вызовы + спецификации);
|
||||||
|
- `tests` слой (сценарии).
|
||||||
|
- Тесты создают сервис напрямую (`new PetService()`), без DI-фреймворка.
|
||||||
|
|
||||||
## Структура проекта
|
## Структура проекта
|
||||||
```plaintext
|
- `src/main/java/ru/otus/petstore/config` — конфигурация
|
||||||
hw3/
|
- `src/main/java/ru/otus/petstore/model` — модели API (`Pet`, `Category`, `Tag`)
|
||||||
├── pom.xml # Maven-конфигурация
|
- `src/main/java/ru/otus/petstore/service` — сервисы API
|
||||||
├── README.md # Readme файл
|
- `src/test/java/ru/otus/petstore/tests` — автотесты (позитивные и негативные сценарии)
|
||||||
└── src
|
- `src/test/java/ru/otus/petstore/util` — фабрика тестовых данных
|
||||||
└── test
|
- `src/test/resources/schemas` — JSON schema
|
||||||
└── java
|
|
||||||
└── ru
|
## Запуск
|
||||||
└── otus
|
### 1. Только тесты
|
||||||
└── petstore
|
```bash
|
||||||
└── PetStoreTests.java # Автотесты
|
mvn test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Обзор
|
### 2. Полная проверка (тесты + Checkstyle + SpotBugs)
|
||||||
|
```bash
|
||||||
|
mvn verify
|
||||||
|
```
|
||||||
|
|
||||||
**Rest-assured** — это библиотека для тестирования REST API, предоставляющая удобный DSL (domain-specific language)
|
## Параметры запуска
|
||||||
для формирования HTTP-запросов, их отправки и проверки ответов. В данном проекте с её помощью отправляются GET и POST
|
- `base.uri` (по умолчанию `https://petstore.swagger.io`)
|
||||||
запросы к Petstore API, а также производятся проверки кодов ответа и содержимого JSON.
|
- `base.path` (по умолчанию `/v2`)
|
||||||
|
|
||||||
### Реализованные сценарии тестирования
|
Пример:
|
||||||
|
```bash
|
||||||
1. **Тест: Поиск питомцев со статусом "available"**
|
mvn "-Dbase.uri=https://petstore.swagger.io" "-Dbase.path=/v2" test
|
||||||
Отправляется GET-запрос на `/pet/findByStatus` с параметром `status=available`.
|
```
|
||||||
**Ожидаемый результат:** HTTP 200 и у всех питомцев в ответе значение поля `status` равно `"available"`.
|
|
||||||
|
|
||||||
2. **Тест: Поиск питомцев по несуществующему статусу**
|
|
||||||
Отправляется GET-запрос с параметром `status=invalidStatus`.
|
|
||||||
**Ожидаемый результат:** HTTP 200 и пустой массив в ответе.
|
|
||||||
|
|
||||||
3. **Тест: Успешное создание питомца**
|
|
||||||
Отправляется POST-запрос с корректными данными для создания питомца.
|
|
||||||
**Ожидаемый результат:** HTTP 200 и возвращаемое поле `name` совпадает с переданным значением.
|
|
||||||
|
|
||||||
4. **Тест: Ошибка при создании питомца без поля "name"**
|
|
||||||
Отправляется POST-запрос, в котором отсутствует обязательное поле `name`.
|
|
||||||
**Ожидаемый результат:** API возвращает ошибку (код 400 или 405).
|
|
||||||
**PS.** Почему-то сервис отдает ответ 200 я решил оставить данный тест.
|
|
||||||
Показать что вся цепочка тестов не останавливается и продолжает работать дальше
|
|
||||||
|
|
||||||
5. **Тест: Ошибка при передаче некорректного JSON**
|
|
||||||
Отправляется POST-запрос с ошибочным JSON (без закрывающей фигурной скобки).
|
|
||||||
**Ожидаемый результат:** API возвращает ошибку (код 400 или 405).
|
|
||||||
|
|
||||||
## Как запустить тесты
|
|
||||||
|
|
||||||
1. **Клонируйте репозиторий:**
|
|
||||||
```bash
|
|
||||||
git clone https://git.kovbasa.ru/otus-autotests/hw3.git
|
|
||||||
|
|
||||||
2. **Перейдите в папку проекта:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd hw3
|
|
||||||
|
|
||||||
3. **Запустите тесты командой:**
|
|
||||||
```bash
|
|
||||||
mvn test
|
|
||||||
|
|
||||||
После выполнения тестов в консоли вы увидите нумерованные сообщения с результатами (например, "1. Test: Find pets by
|
|
||||||
'available' status - Passed").
|
|
||||||
|
|
||||||
## Зависимости
|
|
||||||
- **Rest-assured**: 4.5.1
|
|
||||||
|
|
||||||
- **JUnit Jupiter (API & Engine)**: 5.9.1
|
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
Проект предназначен для образовательных целей.
|
|
||||||
```
|
|
||||||
---
|
|
||||||
Файл REDME.md содержит краткое описание проекта, объясняет суть реализации тестов с Rest-assured, описывает
|
|
||||||
основные сценарии тестирования, структуру проекта и шаги для запуска автотестов. Вы можете его адаптировать под
|
|
||||||
свои нужды или дополнить дополнительной информацией.
|
|
||||||
```
|
|
||||||
|
|||||||
34
checkstyle.xml
Normal file
34
checkstyle.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
|
||||||
|
<module name="FileTabCharacter"/>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="max" value="120"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<module name="TypeName"/>
|
||||||
|
<module name="MethodName"/>
|
||||||
|
<module name="ParameterName"/>
|
||||||
|
<module name="LocalVariableName"/>
|
||||||
|
<module name="MemberName"/>
|
||||||
|
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="UnusedImports"/>
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
|
||||||
|
<module name="NeedBraces"/>
|
||||||
|
|
||||||
|
<module name="WhitespaceAround"/>
|
||||||
|
<module name="WhitespaceAfter"/>
|
||||||
|
|
||||||
|
<module name="EmptyBlock"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
120
pom.xml
120
pom.xml
@@ -1,37 +1,38 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<!-- Основная информация о проекте -->
|
|
||||||
<groupId>ru.otus</groupId>
|
<groupId>ru.otus</groupId>
|
||||||
<artifactId>petstore</artifactId>
|
<artifactId>hw3</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>petstore</name>
|
<name>hw3</name>
|
||||||
|
|
||||||
<!-- Глобальные свойства проекта -->
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Используем Java 24 -->
|
<java.version>21</java.version>
|
||||||
<maven.compiler.source>24</maven.compiler.source>
|
|
||||||
<maven.compiler.target>24</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<!-- Версии зависимостей -->
|
<base.uri>https://petstore.swagger.io</base.uri>
|
||||||
<restassured.version>4.5.1</restassured.version>
|
<base.path>/v2</base.path>
|
||||||
<junit.jupiter.version>5.9.1</junit.jupiter.version>
|
|
||||||
|
<restassured.version>6.0.0</restassured.version>
|
||||||
|
<junit.jupiter.version>6.0.2</junit.jupiter.version>
|
||||||
|
<jackson.version>2.21.0</jackson.version>
|
||||||
|
|
||||||
|
<maven.compiler.version>3.15.0</maven.compiler.version>
|
||||||
|
<surefire.version>3.5.4</surefire.version>
|
||||||
|
<checkstyle.plugin.version>3.6.0</checkstyle.plugin.version>
|
||||||
|
<spotbugs.plugin.version>4.9.8.2</spotbugs.plugin.version>
|
||||||
|
<spotbugs.version>4.9.8</spotbugs.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<!-- Зависимости проекта -->
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Rest-assured для работы с REST API с исключением транзитивой зависимости commons-codec -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.rest-assured</groupId>
|
<groupId>io.rest-assured</groupId>
|
||||||
<artifactId>rest-assured</artifactId>
|
<artifactId>rest-assured</artifactId>
|
||||||
<version>${restassured.version}</version>
|
<version>${restassured.version}</version>
|
||||||
<scope>test</scope>
|
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
@@ -40,56 +41,111 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Явное подключение обновлённой, безопасной версии commons-codec -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
<version>1.15</version>
|
<version>1.21.0</version>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JSON-парсинг через Rest-assured -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.rest-assured</groupId>
|
<groupId>io.rest-assured</groupId>
|
||||||
<artifactId>json-path</artifactId>
|
<artifactId>json-path</artifactId>
|
||||||
<version>${restassured.version}</version>
|
<version>${restassured.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.rest-assured</groupId>
|
||||||
|
<artifactId>json-schema-validator</artifactId>
|
||||||
|
<version>${restassured.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- JUnit Jupiter API для написания автотестов (включает ExtensionContext и TestWatcher) -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
<version>${junit.jupiter.version}</version>
|
<version>${junit.jupiter.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JUnit Jupiter Engine для выполнения тестов -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<version>${junit.jupiter.version}</version>
|
<version>${junit.jupiter.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<!-- Конфигурация сборки проекта -->
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- Maven Compiler Plugin для компиляции проекта с поддержкой Java 24 -->
|
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.11.0</version>
|
<version>${maven.compiler.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>${maven.compiler.source}</source>
|
<release>${java.version}</release>
|
||||||
<target>${maven.compiler.target}</target>
|
<encoding>${project.build.sourceEncoding}</encoding>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- Maven Surefire Plugin для запуска тестов -->
|
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.22.2</version>
|
<version>${surefire.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<useModulePath>false</useModulePath>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<base.uri>${base.uri}</base.uri>
|
||||||
|
<base.path>${base.path}</base.path>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>${checkstyle.plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>checkstyle-validation</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>checkstyle.xml</configLocation>
|
||||||
|
<consoleOutput>true</consoleOutput>
|
||||||
|
<failsOnError>true</failsOnError>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.spotbugs</groupId>
|
||||||
|
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||||
|
<version>${spotbugs.plugin.version}</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.spotbugs</groupId>
|
||||||
|
<artifactId>spotbugs</artifactId>
|
||||||
|
<version>${spotbugs.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
6
spotbugs-exclude.xml
Normal file
6
spotbugs-exclude.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<FindBugsFilter>
|
||||||
|
<Match>
|
||||||
|
<Source name="~.*src/test/java/.*"/>
|
||||||
|
</Match>
|
||||||
|
</FindBugsFilter>
|
||||||
21
src/main/java/ru/otus/petstore/config/TestConfig.java
Normal file
21
src/main/java/ru/otus/petstore/config/TestConfig.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package ru.otus.petstore.config;
|
||||||
|
|
||||||
|
public final class TestConfig {
|
||||||
|
|
||||||
|
private static final String BASE_URI =
|
||||||
|
System.getProperty("base.uri", "https://petstore.swagger.io");
|
||||||
|
|
||||||
|
private static final String BASE_PATH =
|
||||||
|
System.getProperty("base.path", "/v2");
|
||||||
|
|
||||||
|
private TestConfig() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getBaseUri() {
|
||||||
|
return BASE_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getBasePath() {
|
||||||
|
return BASE_PATH;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/ru/otus/petstore/model/Category.java
Normal file
23
src/main/java/ru/otus/petstore/model/Category.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Category.java
|
||||||
|
package ru.otus.petstore.model;
|
||||||
|
|
||||||
|
public class Category {
|
||||||
|
private long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Category() { }
|
||||||
|
|
||||||
|
public Category(long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category(Category other) {
|
||||||
|
this.id = other.id;
|
||||||
|
this.name = other.name;
|
||||||
|
}
|
||||||
|
public long getId() { return id; }
|
||||||
|
public void setId(long id) { this.id = id; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
58
src/main/java/ru/otus/petstore/model/Pet.java
Normal file
58
src/main/java/ru/otus/petstore/model/Pet.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Pet.java
|
||||||
|
package ru.otus.petstore.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Pet {
|
||||||
|
private long id;
|
||||||
|
private Category category;
|
||||||
|
private String name;
|
||||||
|
private List<String> photoUrls;
|
||||||
|
private List<Tag> tags;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
public Pet() { }
|
||||||
|
|
||||||
|
public Pet(long id, Category category, String name,
|
||||||
|
List<String> photoUrls, List<Tag> tags, String status) {
|
||||||
|
this.id = id;
|
||||||
|
this.category = copyCategory(category);
|
||||||
|
this.name = name;
|
||||||
|
this.photoUrls = copyPhotoUrls(photoUrls);
|
||||||
|
this.tags = copyTags(tags);
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() { return id; }
|
||||||
|
public void setId(long id) { this.id = id; }
|
||||||
|
public Category getCategory() { return copyCategory(category); }
|
||||||
|
public void setCategory(Category category) { this.category = copyCategory(category); }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public List<String> getPhotoUrls() { return copyPhotoUrls(photoUrls); }
|
||||||
|
public void setPhotoUrls(List<String> photoUrls) { this.photoUrls = copyPhotoUrls(photoUrls); }
|
||||||
|
public List<Tag> getTags() { return copyTags(tags); }
|
||||||
|
public void setTags(List<Tag> tags) { this.tags = copyTags(tags); }
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
|
||||||
|
private static Category copyCategory(Category source) {
|
||||||
|
return source == null ? null : new Category(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> copyPhotoUrls(List<String> source) {
|
||||||
|
return source == null ? null : List.copyOf(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Tag> copyTags(List<Tag> source) {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Tag> target = new ArrayList<>(source.size());
|
||||||
|
for (Tag tag : source) {
|
||||||
|
target.add(new Tag(tag));
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/ru/otus/petstore/model/Tag.java
Normal file
23
src/main/java/ru/otus/petstore/model/Tag.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Tag.java
|
||||||
|
package ru.otus.petstore.model;
|
||||||
|
|
||||||
|
public class Tag {
|
||||||
|
private long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Tag() { }
|
||||||
|
|
||||||
|
public Tag(long id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag(Tag other) {
|
||||||
|
this.id = other.id;
|
||||||
|
this.name = other.name;
|
||||||
|
}
|
||||||
|
public long getId() { return id; }
|
||||||
|
public void setId(long id) { this.id = id; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
36
src/main/java/ru/otus/petstore/service/BaseService.java
Normal file
36
src/main/java/ru/otus/petstore/service/BaseService.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package ru.otus.petstore.service;
|
||||||
|
|
||||||
|
import io.restassured.builder.RequestSpecBuilder;
|
||||||
|
import io.restassured.builder.ResponseSpecBuilder;
|
||||||
|
import io.restassured.filter.log.LogDetail;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
|
import io.restassured.specification.RequestSpecification;
|
||||||
|
import io.restassured.specification.ResponseSpecification;
|
||||||
|
import ru.otus.petstore.config.TestConfig;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.given;
|
||||||
|
|
||||||
|
public abstract class BaseService {
|
||||||
|
|
||||||
|
protected final RequestSpecification requestSpec = new RequestSpecBuilder()
|
||||||
|
.setBaseUri(TestConfig.getBaseUri())
|
||||||
|
.setBasePath(TestConfig.getBasePath())
|
||||||
|
.setContentType(ContentType.JSON)
|
||||||
|
.log(LogDetail.URI)
|
||||||
|
.log(LogDetail.METHOD)
|
||||||
|
.log(LogDetail.PARAMS)
|
||||||
|
.log(LogDetail.BODY)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
protected final ResponseSpecification okSpec = new ResponseSpecBuilder()
|
||||||
|
.expectStatusCode(200)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
protected final ResponseSpecification notFoundSpec = new ResponseSpecBuilder()
|
||||||
|
.expectStatusCode(404)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
protected RequestSpecification givenBase() {
|
||||||
|
return given().spec(requestSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/main/java/ru/otus/petstore/service/PetService.java
Normal file
72
src/main/java/ru/otus/petstore/service/PetService.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package ru.otus.petstore.service;
|
||||||
|
|
||||||
|
import io.restassured.response.Response;
|
||||||
|
import ru.otus.petstore.model.Pet;
|
||||||
|
|
||||||
|
public class PetService extends BaseService {
|
||||||
|
|
||||||
|
public Response findByStatus(String status) {
|
||||||
|
return givenBase()
|
||||||
|
.queryParam("status", status)
|
||||||
|
.when()
|
||||||
|
.get("/pet/findByStatus");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response createPet(Pet pet) {
|
||||||
|
return givenBase()
|
||||||
|
.body(pet)
|
||||||
|
.when()
|
||||||
|
.post("/pet");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response createPetExpectSuccess(Pet pet) {
|
||||||
|
return createPet(pet)
|
||||||
|
.then()
|
||||||
|
.spec(okSpec)
|
||||||
|
.extract()
|
||||||
|
.response();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response createPetRaw(String rawJson) {
|
||||||
|
return givenBase()
|
||||||
|
.body(rawJson)
|
||||||
|
.when()
|
||||||
|
.post("/pet");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response getPetById(long id) {
|
||||||
|
return givenBase()
|
||||||
|
.when()
|
||||||
|
.get("/pet/{id}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response getPetByIdExpectSuccess(long id) {
|
||||||
|
return getPetById(id)
|
||||||
|
.then()
|
||||||
|
.spec(okSpec)
|
||||||
|
.extract()
|
||||||
|
.response();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response getPetByIdExpectNotFound(long id) {
|
||||||
|
return getPetById(id)
|
||||||
|
.then()
|
||||||
|
.spec(notFoundSpec)
|
||||||
|
.extract()
|
||||||
|
.response();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response deletePet(long id) {
|
||||||
|
return givenBase()
|
||||||
|
.when()
|
||||||
|
.delete("/pet/{id}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePetIfExists(long id) {
|
||||||
|
Response response = deletePet(id);
|
||||||
|
int statusCode = response.statusCode();
|
||||||
|
if (statusCode != 200 && statusCode != 404) {
|
||||||
|
throw new IllegalStateException("Unexpected status for DELETE /pet/{id}: " + statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
package ru.otus.petstore;
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Order;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
|
||||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.junit.jupiter.api.extension.TestWatcher;
|
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static io.restassured.RestAssured.given;
|
|
||||||
import static org.hamcrest.Matchers.*;
|
|
||||||
|
|
||||||
@ExtendWith(PetStoreTests.TestResultLogger.class)
|
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
|
||||||
public class PetStoreTests {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполняется один раз перед запуском всех тестов.
|
|
||||||
* Устанавливаем базовый URI для запросов к Swagger Petstore API.
|
|
||||||
*/
|
|
||||||
@BeforeAll
|
|
||||||
public static void setup() {
|
|
||||||
RestAssured.baseURI = "https://petstore.swagger.io/v2";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тест 1: GET /pet/findByStatus (status = available).
|
|
||||||
* Ожидается статус 200 и все объекты в ответе должны иметь поле "status" равное "available".
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@Order(1)
|
|
||||||
@DisplayName("1. Test: Find pets by 'available' status")
|
|
||||||
public void testFindPetsByAvailableStatus() {
|
|
||||||
given()
|
|
||||||
.queryParam("status", "available")
|
|
||||||
.when()
|
|
||||||
.get("/pet/findByStatus")
|
|
||||||
.then()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("status", everyItem(equalTo("available")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тест 2: GET /pet/findByStatus с несуществующим статусом "invalidStatus".
|
|
||||||
* Ожидается статус 200 и пустой массив в ответе.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@Order(2)
|
|
||||||
@DisplayName("2. Test: Find pets by invalid status")
|
|
||||||
public void testFindPetsByInvalidStatus() {
|
|
||||||
given()
|
|
||||||
.queryParam("status", "invalidStatus")
|
|
||||||
.when()
|
|
||||||
.get("/pet/findByStatus")
|
|
||||||
.then()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("", hasSize(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тест 3: POST /pet для создания питомца с валидными данными.
|
|
||||||
* Ожидается статус 200 и возвращаемое поле "name" совпадает с переданным.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@Order(3)
|
|
||||||
@DisplayName("3. Test: Create pet successfully")
|
|
||||||
public void testCreatePetSuccess() {
|
|
||||||
int petId = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);
|
|
||||||
String petName = "Rex";
|
|
||||||
String requestBody = "{\n" +
|
|
||||||
" \"id\": " + petId + ",\n" +
|
|
||||||
" \"category\": { \"id\": 1, \"name\": \"Dogs\" },\n" +
|
|
||||||
" \"name\": \"" + petName + "\",\n" +
|
|
||||||
" \"photoUrls\": [ \"http://example.com/photo.jpg\" ],\n" +
|
|
||||||
" \"tags\": [ { \"id\": 0, \"name\": \"string\" } ],\n" +
|
|
||||||
" \"status\": \"available\"\n" +
|
|
||||||
"}";
|
|
||||||
given()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.body(requestBody)
|
|
||||||
.when()
|
|
||||||
.post("/pet")
|
|
||||||
.then()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("name", equalTo(petName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тест 4: POST /pet для создания питомца с невалидными данными (без поля "name").
|
|
||||||
* Ожидается, что API вернёт ошибку (400 или 405). Почему-то сервис отдает ответ 200
|
|
||||||
* я решил оставить данный тест. Показать что вся цепочка тестов не останавливается и
|
|
||||||
* продолжает работать дальше
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@Order(4)
|
|
||||||
@DisplayName("4. Test: Fail to create pet without 'name' field")
|
|
||||||
public void testCreatePetMissingName() {
|
|
||||||
int petId = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);
|
|
||||||
String requestBody = "{\n" +
|
|
||||||
" \"id\": " + petId + ",\n" +
|
|
||||||
" \"category\": { \"id\": 1, \"name\": \"Dogs\" },\n" +
|
|
||||||
" \"photoUrls\": [ \"http://example.com/photo.jpg\" ],\n" +
|
|
||||||
" \"tags\": [ { \"id\": 0, \"name\": \"string\" } ],\n" +
|
|
||||||
" \"status\": \"available\"\n" +
|
|
||||||
"}";
|
|
||||||
given()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.body(requestBody)
|
|
||||||
.when()
|
|
||||||
.post("/pet")
|
|
||||||
.then()
|
|
||||||
.statusCode(anyOf(is(400), is(405)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Тест 5: POST /pet с некорректным JSON (отсутствует закрывающая фигурная скобка).
|
|
||||||
* Ожидается, что API вернёт ошибку (400 или 405).
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@Order(5)
|
|
||||||
@DisplayName("5. Test: Create pet with invalid JSON")
|
|
||||||
public void testCreatePetInvalidJson() {
|
|
||||||
String invalidJson = """
|
|
||||||
{
|
|
||||||
"id": 123456,
|
|
||||||
"name": "Invalid Pet",
|
|
||||||
"category": { "id": 1, "name": "Dogs" },
|
|
||||||
"photoUrls": [ "http://example.com/photo.jpg" ],
|
|
||||||
"tags": [ { "id": 0, "name": "string" } ],
|
|
||||||
"status": "available"
|
|
||||||
"""; // отсутствует закрывающая фигурная скобка
|
|
||||||
given()
|
|
||||||
.contentType(ContentType.JSON)
|
|
||||||
.body(invalidJson)
|
|
||||||
.when()
|
|
||||||
.post("/pet")
|
|
||||||
.then()
|
|
||||||
.statusCode(anyOf(is(400), is(405)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Реализация TestWatcher для логирования результата каждого теста.
|
|
||||||
* Выводит сообщения: если тест пройден – "Test Passed", иначе "Test Failed", "Test Aborted" или "Test Disabled".
|
|
||||||
*/
|
|
||||||
public static class TestResultLogger implements TestWatcher {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void testSuccessful(ExtensionContext context) {
|
|
||||||
System.out.println(context.getDisplayName() + " - Passed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void testFailed(ExtensionContext context, Throwable cause) {
|
|
||||||
System.out.println(context.getDisplayName() + " - Failed: " + cause.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void testAborted(ExtensionContext context, Throwable cause) {
|
|
||||||
System.out.println(context.getDisplayName() + " - Aborted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void testDisabled(ExtensionContext context, Optional<String> reason) {
|
|
||||||
System.out.println(context.getDisplayName() + " - Disabled: " + reason.orElse("No reason provided"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package ru.otus.petstore.tests;
|
||||||
|
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import ru.otus.petstore.model.Pet;
|
||||||
|
import ru.otus.petstore.service.PetService;
|
||||||
|
import ru.otus.petstore.util.PetFactory;
|
||||||
|
|
||||||
|
public class PetNegativeScenariosTest {
|
||||||
|
|
||||||
|
private final PetService petService = new PetService();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяем POST /pet с некорректным JSON:
|
||||||
|
* - API не принимает сломанное тело запроса;
|
||||||
|
* - возвращается клиентская/серверная ошибка вместо 200.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("POST /pet with malformed JSON returns error status")
|
||||||
|
void createPetWithMalformedJsonReturnsError() {
|
||||||
|
String brokenJson = """
|
||||||
|
{
|
||||||
|
"id": 123456,
|
||||||
|
"name": "Broken pet",
|
||||||
|
"photoUrls": ["https://petstore.test/photo.jpg"]
|
||||||
|
""";
|
||||||
|
|
||||||
|
int status = petService.createPetRaw(brokenJson).statusCode();
|
||||||
|
MatcherAssert.assertThat(status, Matchers.anyOf(Matchers.is(400), Matchers.is(405), Matchers.is(500)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяем GET /pet/{id} для несуществующего ресурса:
|
||||||
|
* - сервер возвращает 404;
|
||||||
|
* - сообщение об ошибке соответствует "Pet not found".
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("GET /pet/{id} returns 404 for unknown id")
|
||||||
|
void getMissingPetReturnsNotFound() {
|
||||||
|
long unknownId = PetFactory.nextId();
|
||||||
|
Pet createdPet = PetFactory.newAvailablePet(unknownId);
|
||||||
|
|
||||||
|
// Удаляем id на старте теста, чтобы гарантировать отсутствие сущности.
|
||||||
|
petService.deletePetIfExists(unknownId);
|
||||||
|
|
||||||
|
// Создаем и сразу удаляем питомца, чтобы не пересекаться с возможными данными окружения.
|
||||||
|
petService.createPetExpectSuccess(createdPet);
|
||||||
|
petService.deletePetIfExists(unknownId);
|
||||||
|
|
||||||
|
var response = petService.getPetByIdExpectNotFound(unknownId);
|
||||||
|
MatcherAssert.assertThat(response.path("message"), Matchers.equalTo("Pet not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяем GET /pet/findByStatus с невалидным фильтром:
|
||||||
|
* - API возвращает успешный код;
|
||||||
|
* - выдача пустая.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("GET /pet/findByStatus with unknown status returns empty list")
|
||||||
|
void findByInvalidStatusReturnsEmpty() {
|
||||||
|
petService.findByStatus("invalidStatus")
|
||||||
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.body("$", Matchers.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package ru.otus.petstore.tests;
|
||||||
|
|
||||||
|
import io.restassured.response.Response;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import ru.otus.petstore.model.Pet;
|
||||||
|
import ru.otus.petstore.service.PetService;
|
||||||
|
import ru.otus.petstore.util.PetFactory;
|
||||||
|
|
||||||
|
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
public class PetPositiveScenariosTest {
|
||||||
|
|
||||||
|
private final PetService petService = new PetService();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяем сценарий POST /pet + GET /pet/{id}:
|
||||||
|
* - питомец создается успешно;
|
||||||
|
* - по id возвращается именно созданный объект.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("POST /pet creates resource and GET /pet/{id} returns created pet")
|
||||||
|
void createPetAndGetById() {
|
||||||
|
long petId = PetFactory.nextId();
|
||||||
|
Pet pet = PetFactory.newAvailablePet(petId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Response createResponse = petService.createPetExpectSuccess(pet);
|
||||||
|
assertThat(createResponse.path("id"), equalTo((int) petId));
|
||||||
|
assertThat(createResponse.path("name"), equalTo(pet.getName()));
|
||||||
|
|
||||||
|
Response getResponse = retryGetPetById(petService, petId);
|
||||||
|
assertThat(getResponse.path("id"), equalTo((int) petId));
|
||||||
|
assertThat(getResponse.path("name"), equalTo(pet.getName()));
|
||||||
|
} finally {
|
||||||
|
petService.deletePetIfExists(petId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяем GET /pet/{id}:
|
||||||
|
* - успешный ответ для существующего питомца;
|
||||||
|
* - тело ответа соответствует JSON-схеме Pet.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@DisplayName("GET /pet/{id} returns valid payload by schema")
|
||||||
|
void getPetByIdMatchesSchema() {
|
||||||
|
long petId = PetFactory.nextId();
|
||||||
|
Pet pet = PetFactory.newAvailablePet(petId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
petService.createPetExpectSuccess(pet);
|
||||||
|
|
||||||
|
petService.getPetByIdExpectSuccess(petId)
|
||||||
|
.then()
|
||||||
|
.body(matchesJsonSchemaInClasspath("schemas/pet-schema.json"));
|
||||||
|
} finally {
|
||||||
|
petService.deletePetIfExists(petId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response retryGetPetById(PetService petService, long petId) {
|
||||||
|
int maxAttempts = 5;
|
||||||
|
for (int i = 0; i < maxAttempts; i++) {
|
||||||
|
Response response = petService.getPetById(petId);
|
||||||
|
if (response.statusCode() == 200) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
sleep(300L);
|
||||||
|
}
|
||||||
|
return petService.getPetByIdExpectSuccess(petId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(long millis) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
} catch (InterruptedException interruptedException) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IllegalStateException("Sleep interrupted", interruptedException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/test/java/ru/otus/petstore/util/PetFactory.java
Normal file
30
src/test/java/ru/otus/petstore/util/PetFactory.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package ru.otus.petstore.util;
|
||||||
|
|
||||||
|
import ru.otus.petstore.model.Category;
|
||||||
|
import ru.otus.petstore.model.Pet;
|
||||||
|
import ru.otus.petstore.model.Tag;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
public final class PetFactory {
|
||||||
|
private static final String TEST_PHOTO_URL = "https://petstore.test/photo.jpg";
|
||||||
|
|
||||||
|
private PetFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long nextId() {
|
||||||
|
return ThreadLocalRandom.current().nextLong(1_000_000L, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pet newAvailablePet(long id) {
|
||||||
|
return new Pet(
|
||||||
|
id,
|
||||||
|
new Category(1L, "Dogs"),
|
||||||
|
"Rex-" + id,
|
||||||
|
List.of(TEST_PHOTO_URL),
|
||||||
|
List.of(new Tag(1L, "api-test")),
|
||||||
|
"available"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/test/resources/schemas/pet-schema.json
Normal file
52
src/test/resources/schemas/pet-schema.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"photoUrls"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"photoUrls": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"minItems": 1
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user