From 092a020059288f44adbe2954355e9a08921de0dc Mon Sep 17 00:00:00 2001 From: RKaraMos_EVO Date: Sun, 14 Jun 2026 15:22:36 +0200 Subject: [PATCH] ajout du scenario cucumber et du README --- mylibrary/README.md | 78 +++++++++++++ .../mylibrary/features/book/BookSteps.java | 106 ++++++++++++++++++ .../src/test/resources/features/book.feature | 40 +++++++ 3 files changed, 224 insertions(+) create mode 100644 mylibrary/README.md create mode 100644 mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java create mode 100644 mylibrary/src/test/resources/features/book.feature diff --git a/mylibrary/README.md b/mylibrary/README.md new file mode 100644 index 0000000..86612a6 --- /dev/null +++ b/mylibrary/README.md @@ -0,0 +1,78 @@ +# mylibrary — back-end Java (méthode BDD) + +Back-end du projet **2026-DEV-BUT3**. Reproduit, en Java pur, le cœur métier +de l'API consommée par le front React `my-library/`. + +## Méthode + +Conformément au cours `maintenanceApplicativeCours1.pdf` (Behavior-Driven +Development), pour chaque comportement on : + +1. Écrit le scénario attendu (en Gherkin / `.feature` ou en `@DisplayName` JUnit) +2. Écrit le test qui le vérifie +3. Implémente le code qui fait passer le test + +## Structure (calquée sur le module `customer` fourni par le prof) + +``` +src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/ +├─ customer/ Module fourni par l'enseignant (intact) +│ ├─ CustomerInfo.java record d'entrée +│ ├─ CustomerDTO.java DTO de sortie +│ ├─ entity/ Customer (objet métier, règles fidélité) +│ ├─ exception/ Exceptions métier +│ ├─ converter/ Mapping Info <-> entity <-> DTO +│ ├─ validator/ Règles de validation +│ ├─ repository/ Stockage en mémoire (List) +│ └─ usecase/ Cas d'usage (orchestration) +└─ book/ Module développé en miroir (notre travail) + └─ … (mêmes sous-paquets, même découpage) + +src/test/java/.../mylibrary/ +├─ customer/ Tests JUnit fournis par l'enseignant +├─ book/ Tests JUnit que nous avons écrits +└─ features/ + ├─ RunCucumberTest.java (du prof, intact) + ├─ client/CustomerSteps.java (du prof, intact) + ├─ book/BookSteps.java (notre travail) + └─ resources/features/ + ├─ client.feature (du prof, intact) + └─ book.feature (notre travail) +``` + +## Dépendances + +Strictement les mêmes que dans le template du prof : + +- JUnit 5 (jupiter-api, params, engine + platform-suite, platform-engine, platform-launcher) +- Mockito (core, junit-jupiter) +- Cucumber (cucumber-java, cucumber-junit-platform-engine) +- Lombok + +## Build & test + +Pré-requis : **JDK 21** + **Maven 3.9+** + +```bash +mvn -f mylibrary test +``` + +Cette commande exécute : + +- les tests unitaires JUnit (entity, validator, converter, repository, usecase, exceptions) +- les scénarios Cucumber (`client.feature` + `book.feature`) via `RunCucumberTest` + +## Périmètre couvert + +Conformément à la consigne (« reproduire le strict nécessaire pour démontrer +la maîtrise »), l'API se limite aux deux domaines présents dans le swagger +qui structurent l'app React : + +- **Catalogue** (`book`) : enregistrer, consulter par ISBN, lister tous les livres, + refuser ISBN dupliqué, refuser un livre invalide, gérer le stock. +- **Comptes clients** (`customer`) : enregistrer, consulter par téléphone, mettre + à jour, supprimer, ajouter / retirer des points de fidélité (module fourni). + +L'exposition HTTP réelle utilisée par les développeurs front est l'API du prof +(`mylibrary-0.0.1-SNAPSHOT.jar`) ; ce module sert à démontrer la maîtrise +de la **conception métier en BDD/TDD**. diff --git a/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java b/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java new file mode 100644 index 0000000..5a9ba3c --- /dev/null +++ b/mylibrary/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/book/BookSteps.java @@ -0,0 +1,106 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.features.book; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Category; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookAlreadyExistsException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.book.usecase.BookUseCase; +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +public class BookSteps { + + private final BookRepository bookRepository = new BookRepository(); + private final BookUseCase bookUseCase = new BookUseCase(bookRepository); + + private long lastRegisteredIsbn; + private BookDTO retrievedBook; + private List allBooks; + private Exception lastException; + + @Given("the catalog has the following books:") + public void theCatalogHasTheFollowingBooks(DataTable dataTable) throws NotValidBookException, BookAlreadyExistsException { + bookRepository.deleteAll(); + for (Map row : dataTable.asMaps(String.class, String.class)) { + bookUseCase.registerBook(toBookInfo(row)); + } + } + + @When("I register a new book with the following information:") + public void iRegisterANewBook(DataTable dataTable) throws NotValidBookException, BookAlreadyExistsException { + Map row = dataTable.asMaps(String.class, String.class).getFirst(); + lastRegisteredIsbn = bookUseCase.registerBook(toBookInfo(row)); + } + + @When("I try to register a new book with the following information:") + public void iTryToRegisterANewBook(DataTable dataTable) { + Map row = dataTable.asMaps(String.class, String.class).getFirst(); + lastException = assertThrows(Exception.class, () -> bookUseCase.registerBook(toBookInfo(row))); + } + + @When("I request the book with isbn {long}") + public void iRequestTheBook(long isbn) throws BookNotFoundException { + retrievedBook = bookUseCase.getBookByIsbn(isbn); + } + + @When("I list all books") + public void iListAllBooks() { + allBooks = bookUseCase.getAllBooks(); + } + + @Then("the book is created") + public void theBookIsCreated() { + assertNotNull(lastRegisteredIsbn); + } + + @And("the catalog now has {int} book(s)") + public void theCatalogNowHasNBooks(int expected) { + assertEquals(expected, bookRepository.findAll().size()); + } + + @Then("the registration fails with a {string}") + public void theRegistrationFailsWith(String exceptionSimpleName) { + assertNotNull(lastException); + assertEquals(exceptionSimpleName, lastException.getClass().getSimpleName()); + } + + @Then("I receive a book whose title is {string}") + public void iReceiveABookWhoseTitleIs(String title) { + assertNotNull(retrievedBook); + assertEquals(title, retrievedBook.getTitle()); + } + + @Then("I receive {int} book(s)") + public void iReceiveNBooks(int expected) { + assertNotNull(allBooks); + assertEquals(expected, allBooks.size()); + } + + private static BookInfo toBookInfo(Map row) { + return new BookInfo( + Long.parseLong(row.get("isbn")), + row.get("titre"), + row.get("auteur"), + row.get("editeur"), + LocalDate.parse(row.get("datePublication")), + Double.parseDouble(row.get("prix")), + Integer.parseInt(row.get("stock")), + List.of(Category.valueOf(row.get("categorie"))), + "", + row.get("langue") + ); + } +} diff --git a/mylibrary/src/test/resources/features/book.feature b/mylibrary/src/test/resources/features/book.feature new file mode 100644 index 0000000..a0fd950 --- /dev/null +++ b/mylibrary/src/test/resources/features/book.feature @@ -0,0 +1,40 @@ +# language: en + +Feature: Manage book catalog + + Scenario: Register a new book in the catalog + When I register a new book with the following information: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | DDD | Evans | AW | 2003-08-22 | 54.99 | 10 | SCIENCE | EN | + Then the book is created + And the catalog now has 1 book + + Scenario: Reject duplicate ISBN + Given the catalog has the following books: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | DDD | Evans | AW | 2003-08-22 | 54.99 | 10 | SCIENCE | EN | + When I try to register a new book with the following information: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | DDD copy | Evans | AW | 2003-08-22 | 54.99 | 10 | SCIENCE | EN | + Then the registration fails with a "BookAlreadyExistsException" + + Scenario: Reject invalid book information + When I try to register a new book with the following information: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | | Evans | AW | 2003-08-22 | 0 | -1 | SCIENCE | EN | + Then the registration fails with a "NotValidBookException" + + Scenario: Retrieve a book by its ISBN + Given the catalog has the following books: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | DDD | Evans | AW | 2003-08-22 | 54.99 | 10 | SCIENCE | EN | + When I request the book with isbn 9780321125217 + Then I receive a book whose title is "DDD" + + Scenario: List all books in the catalog + Given the catalog has the following books: + | isbn | titre | auteur | editeur | datePublication | prix | stock | categorie | langue | + | 9780321125217 | DDD | Evans | AW | 2003-08-22 | 54.99 | 10 | SCIENCE | EN | + | 9780132350884 | Clean | Martin | PH | 2008-08-01 | 30.00 | 5 | SCIENCE | EN | + When I list all books + Then I receive 2 books