Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,26 @@ docker compose up -d

mvn test -Dgroups=integration -DexcludedGroups=
```

### Allure-отчёт

Просмотр по умолчанию — по штатному прогону, без живого бэкенда
(`integration`-группа исключена, живого бэкенда в конвейере нет):

```bash
mvn test
mvn io.qameta.allure:allure-maven:2.17.0:report
# открыть target/allure-report/index.html
```

Полный отчёт с live-интеграцией собирается только явным запуском против
локального бэкенда. Требования окружения: поднятый бэкенд на `localhost:8080`
(см. «Запуск с реальным бэкендом» выше) и `.env` с `IT_ADMIN_EMAIL` /
`IT_ADMIN_PASSWORD` сид-админа локального бэкенда (без пароля live-тесты
скипаются):

```bash
mvn test -Dgroups=integration -DexcludedGroups=
mvn io.qameta.allure:allure-maven:2.17.0:report
# открыть target/allure-report/index.html
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.DesktopApplicationClientJava.services.ServiceException;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

/**
Expand All @@ -13,9 +18,15 @@
* explicitly: {@code mvn test -Dgroups=integration -DexcludedGroups=} ({@code @Tag} inherited from
* IntegrationTestCase).
*/
@Epic("Проверка ввода")
@Feature("Вход (email + пароль)")
@Story("Страница входа")
@Tag("role:admin")
@DisplayName("Вход через живой API")
class LoginIntegrationTest extends IntegrationTestCase {

@Test
@DisplayName("Чужие учётные данные отклоняются живым API")
void invalidCredentialsAreRejectedByRealApi() {
assertThrows(
ServiceException.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

import com.DesktopApplicationClientJava.entities.Resource;
import com.DesktopApplicationClientJava.services.ServiceException;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

/**
Expand All @@ -18,13 +23,20 @@
* explicitly: {@code mvn test -Dgroups=integration -DexcludedGroups=} ({@code @Tag} inherited from
* IntegrationTestCase).
*/
@Epic("Ресурс")
@Feature("Просмотр метаданных ресурса")
@Story("Страница деталей ресурса")
@Tag("role:admin")
@DisplayName("Детали ресурса через живой API")
class ResourceDetailsIntegrationTest extends IntegrationTestCase {
@Test
@DisplayName("Неизвестный идентификатор бросает сервисную ошибку")
void unknownIdThrowsServiceException() {
assertThrows(ServiceException.class, () -> resourceService.getById(UUID.randomUUID()));
}

@Test
@DisplayName("Созданный ресурс читается обратно")
void createdResourceRoundTrip() {
assumeTrue(
adminPassword != null && !adminPassword.isBlank(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import com.DesktopApplicationClientJava.session.Session;
import com.DesktopApplicationClientJava.ui.FxTestBase;
import com.DesktopApplicationClientJava.utils.ControllerWiring;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import java.util.UUID;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
Expand All @@ -35,6 +38,8 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.testfx.api.FxRobot;
import org.testfx.api.FxToolkit;
Expand All @@ -52,6 +57,11 @@
* excludedGroups} in pom.xml). Run explicitly: {@code mvn test -Dgroups=integration
* -DexcludedGroups= -Dtest=ResourceFormIntegrationTest}.
*/
@Epic("Ресурс")
@Feature("Создание и правка ресурса")
@Story("Страница формы ресурса")
@Tag("role:admin")
@DisplayName("Форма ресурса через живой API")
class ResourceFormIntegrationTest extends IntegrationTestCase {

private final FxRobot robot = new FxRobot();
Expand Down Expand Up @@ -99,6 +109,7 @@ void setUpServices() {
}

@Test
@DisplayName("Создание с корректными полями читается обратно")
void createValidRoundTrip() {
loginAsAdmin();
String title = "IT ResourceForm " + UUID.randomUUID();
Expand All @@ -113,6 +124,7 @@ void createValidRoundTrip() {
}

@Test
@DisplayName("Создание без описания читается обратно")
void createNullDescriptionRoundTrip() {
loginAsAdmin();
String title = "IT ResourceForm " + UUID.randomUUID();
Expand All @@ -125,6 +137,7 @@ void createNullDescriptionRoundTrip() {
}

@Test
@DisplayName("Правка названия и описания сохраняется")
void updateValidRoundTrip() {
loginAsAdmin();
String updatedTitle = "IT ResourceForm updated " + UUID.randomUUID();
Expand All @@ -141,6 +154,7 @@ void updateValidRoundTrip() {
}

@Test
@DisplayName("Некорректные названия отклоняются с 400")
void invalidTitlesAreRejectedWith400() {
loginAsAdmin();

Expand All @@ -150,6 +164,7 @@ void invalidTitlesAreRejectedWith400() {
}

@Test
@DisplayName("Пустое описание отклоняется с 400")
void emptyDescriptionIsRejectedWith400() {
loginAsAdmin();

Expand All @@ -159,13 +174,15 @@ void emptyDescriptionIsRejectedWith400() {
}

@Test
@DisplayName("Правка неизвестного ресурса возвращает 404")
void updateUnknownIdReturns404() {
loginAsAdmin();

assertStatus(404, () -> resourceService.update(UUID.randomUUID(), "IT ResourceForm", null));
}

@Test
@DisplayName("Сохранение с чужими токенами показывает ошибку без перехода")
void saveWithInvalidTokensShowsErrorAndDoesNotNavigate() {
// Garbage tokens (not a fresh session) force a real HTTP 401 round-trip instead of the
// client's offline "no refresh token" shortcut, proving server-side auth enforcement.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import com.DesktopApplicationClientJava.session.Session;
import com.DesktopApplicationClientJava.ui.FxTestBase;
import com.DesktopApplicationClientJava.utils.ControllerWiring;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
Expand All @@ -40,6 +43,8 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.testfx.api.FxRobot;
import org.testfx.api.FxToolkit;
Expand All @@ -54,6 +59,10 @@
* the default build and CI (see surefire {@code excludedGroups} in pom.xml). Run explicitly: {@code
* mvn test -Dgroups=integration -DexcludedGroups= -Dtest=ResourceListIntegrationTest}.
*/
@Epic("Ресурс")
@Feature("Список ресурсов")
@Story("Страница списка ресурсов")
@DisplayName("Список ресурсов через живой API")
class ResourceListIntegrationTest extends IntegrationTestCase {

private final FxRobot robot = new FxRobot();
Expand Down Expand Up @@ -103,6 +112,8 @@ void setUpServices() {
}

@Test
@Tag("role:admin")
@DisplayName("Пустой поиск показывает заглушку и нулевой счётчик")
void emptySearchShowsPlaceholderAndZeroCounter() {
loginAsAdmin();
loadListPage(robot);
Expand All @@ -116,6 +127,8 @@ void emptySearchShowsPlaceholderAndZeroCounter() {
}

@Test
@Tag("role:admin")
@DisplayName("Созданный ресурс находится поиском")
void seededResourceAppearsInSearch() {
loginAsAdmin();
String title = "IT ResourceList " + UUID.randomUUID();
Expand All @@ -136,6 +149,8 @@ void seededResourceAppearsInSearch() {
}

@Test
@Tag("role:admin")
@DisplayName("Сортировка по названию упорядочивает ресурсы")
void sortByTitleAscOrdersSeededResources() {
loginAsAdmin();
String prefix = "IT ResourceSort " + UUID.randomUUID() + " ";
Expand All @@ -161,6 +176,8 @@ void sortByTitleAscOrdersSeededResources() {
}

@Test
@Tag("role:admin")
@DisplayName("Поиск без входа показывает ошибку и пустую таблицу")
void unauthenticatedSearchShowsErrorAndLeavesTableEmpty() {
// Fresh session, no login: the request cannot be authenticated.
session.clear();
Expand All @@ -182,6 +199,8 @@ void unauthenticatedSearchShowsErrorAndLeavesTableEmpty() {
* that lenient contract: unknown sort fields must still return 200 ordered by creation time.
*/
@Test
@Tag("role:admin")
@DisplayName("Неизвестное поле сортировки откатывается к дате создания")
void invalidSortFieldFallsBackToCreatedAtDesc() {
loginAsAdmin();
String prefix = "IT SortFallback " + UUID.randomUUID() + " ";
Expand All @@ -206,6 +225,8 @@ void invalidSortFieldFallsBackToCreatedAtDesc() {
* модалку, подтверждение закрывает её и удаляет ресурс на бэкенде.
*/
@Test
@Tag("role:admin")
@DisplayName("Удаление через модалку убирает ресурс с бэкенда")
void deleteCycleRemovesResourceFromBackend() {
loginAsAdmin();
String title = "IT ResourceDelete " + UUID.randomUUID();
Expand Down Expand Up @@ -240,6 +261,8 @@ void deleteCycleRemovesResourceFromBackend() {
}

@Test
@Tag("role:moder")
@DisplayName("Удаление глазами модератора")
@Disabled("Невоспроизводим: в дев-бэкенде засеян только ADMIN, сида MODER нет")
void deleteCycleAsModerator() {
// Живой цикл удаления за МОДЕР (не админа) невоспроизводим: интеграционное окружение
Expand All @@ -255,6 +278,8 @@ private void loginAsAdmin() {
}

@Test
@Tag("role:admin")
@DisplayName("Поиск по описанию находит ресурс")
void searchByDescriptionFindsSeededResource() {
loginAsAdmin();
String description = "IT описание " + UUID.randomUUID();
Expand All @@ -277,6 +302,8 @@ void searchByDescriptionFindsSeededResource() {
}

@Test
@Tag("role:admin")
@DisplayName("Отмена удаления оставляет ресурс на бэкенде")
void cancelledDeleteKeepsResourceOnBackend() {
loginAsAdmin();
String title = "IT ResourceCancel " + UUID.randomUUID();
Expand Down
Loading