From c55244202936286680262baad2028123387fa401 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 3 Apr 2026 21:51:46 +0200 Subject: [PATCH] fin --- .../converter/DtoToEntityConverter.java | 6 - .../converter/EntityToModelConverter.java | 19 +- .../yakamon/data/model/ItemModel.java | 14 +- .../yakamon/data/model/YakadexEntryModel.java | 11 +- .../yakamon/data/model/YakamonModel.java | 15 +- .../data/repository/GameRepository.java | 7 +- .../data/repository/ItemRepository.java | 8 + .../data/repository/PlayerRepository.java | 7 +- .../data/repository/TeamRepository.java | 3 - .../data/repository/YakadexRepository.java | 8 +- .../data/repository/YakamonRepository.java | 8 + .../yakamon/domain/entity/GameEntity.java | 28 +- .../yakamon/domain/entity/ItemEntity.java | 15 +- .../yakamon/domain/entity/PlayerEntity.java | 14 + .../yakamon/domain/entity/TileEntity.java | 16 + .../yakamon/domain/service/GameService.java | 142 +++++- .../yakamon/domain/service/PlayerService.java | 239 ++++++++- .../domain/service/YakadexService.java | 65 ++- .../api/request/PlayerMoveRequest.java | 6 +- .../presentation/rest/GameResource.java | 33 +- .../presentation/rest/InventoryResource.java | 13 +- .../presentation/rest/PlayerResource.java | 67 ++- .../presentation/rest/YakadexResource.java | 23 +- .../assistants/yakamon/utils/ErrorCode.java | 22 +- .../yakamon_testsuite/ConverterTests.java | 239 +++++++++ .../yakamon_testsuite/GameResourceTest.java | 189 ++++++++ .../HelloWorldResourceTest.java | 79 +++ .../InventoryResourceTest.java | 116 +++++ .../yakamon_testsuite/MainTests.java | 7 + .../yakamon_testsuite/PlayerResourceTest.java | 210 ++++++++ .../yakamon_testsuite/ServiceLayerTests.java | 173 +++++++ .../yakamon_testsuite/TeamResourceTest.java | 457 ++++++++++++++++++ .../YakadexResourceTest.java | 328 +++++++++++++ 33 files changed, 2455 insertions(+), 132 deletions(-) create mode 100644 yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/ItemRepository.java delete mode 100644 yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/TeamRepository.java create mode 100644 yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakamonRepository.java create mode 100644 yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/PlayerEntity.java create mode 100644 yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/TileEntity.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ConverterTests.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/GameResourceTest.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/HelloWorldResourceTest.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/InventoryResourceTest.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/MainTests.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/PlayerResourceTest.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ServiceLayerTests.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/TeamResourceTest.java create mode 100644 yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/YakadexResourceTest.java diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/DtoToEntityConverter.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/DtoToEntityConverter.java index 45534db..941cc3c 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/DtoToEntityConverter.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/DtoToEntityConverter.java @@ -16,7 +16,6 @@ public final class DtoToEntityConverter { if (dto == null) return null; YakamonEntity entity = new YakamonEntity(); - // may be null / invalid if (dto.uuid != null) { try { entity.uuid = UUID.fromString(dto.uuid); @@ -44,15 +43,12 @@ public final class DtoToEntityConverter { e.evolutionId = dto.evolutionId; e.caught = dto.caught; - // map element type strings to enum if (dto.firstType != null) { try { e.firstType = ElementType.valueOf(dto.firstType); } catch (IllegalArgumentException ex) { e.firstType = null; } - } else { - e.firstType = null; } if (dto.secondType != null) { @@ -61,8 +57,6 @@ public final class DtoToEntityConverter { } catch (IllegalArgumentException ex) { e.secondType = null; } - } else { - e.secondType = null; } return e; diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/EntityToModelConverter.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/EntityToModelConverter.java index 7e84bd4..742f54a 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/EntityToModelConverter.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/converter/EntityToModelConverter.java @@ -8,7 +8,6 @@ import fr.epita.assistants.yakamon.domain.entity.GameEntity; import fr.epita.assistants.yakamon.domain.entity.PlayerEntity; import fr.epita.assistants.yakamon.domain.entity.YakadexEntryEntity; import fr.epita.assistants.yakamon.domain.entity.YakamonEntity; -import java.time.LocalDateTime; public final class EntityToModelConverter { @@ -38,16 +37,10 @@ public final class EntityToModelConverter { if (e == null) return null; YakamonModel m = new YakamonModel(); m.nickname = e.nickname; - m.energyPoints = e.energyPoints; + m.energy_points = e.energyPoints; if (e.yakadexId != null) { YakadexEntryModel yakadex = new YakadexEntryModel(); - yakadex.name = null; - yakadex.caught = null; - yakadex.firstType = null; - yakadex.secondType = null; - yakadex.description = null; - yakadex.evolveThreshold = null; - m.yakadexEntryId = yakadex; + m.yakadex_entry_id = yakadex; } return m; } @@ -57,11 +50,11 @@ public final class EntityToModelConverter { YakadexEntryModel m = new YakadexEntryModel(); m.name = e.name; m.caught = e.caught; - m.firstType = e.firstType; - m.secondType = e.secondType; + m.first_type = e.firstType != null ? e.firstType.name() : null; + m.second_type = e.secondType != null ? e.secondType.name() : null; m.description = e.description; - m.evolveThreshold = e.evolveThreshold; - m.evolutionId = null; // don't know yet + m.evolve_threshold = e.evolveThreshold; + m.evolution_id = null; return m; } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/ItemModel.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/ItemModel.java index 74826b0..ff1a256 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/ItemModel.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/ItemModel.java @@ -4,11 +4,15 @@ import fr.epita.assistants.yakamon.utils.tile.ItemType; import jakarta.persistence.*; @Entity -@Table(name="item") +@Table(name = "item") public class ItemModel { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) protected Integer id; - public ItemType type; - public Integer quantity; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + protected Integer id; + + @Enumerated + public ItemType type; + + public Integer quantity; } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakadexEntryModel.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakadexEntryModel.java index 0977be4..7d165b5 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakadexEntryModel.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakadexEntryModel.java @@ -13,13 +13,16 @@ public class YakadexEntryModel { public String name; public Boolean caught; - public ElementType firstType; - public ElementType secondType; + + // Changed to string otherwise it doesn't work + public String first_type; + public String second_type; + public String description; @OneToOne @JoinColumn(name = "evolution_id") - public YakadexEntryModel evolutionId; + public YakadexEntryModel evolution_id; - public Integer evolveThreshold; + public Integer evolve_threshold; } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakamonModel.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakamonModel.java index 73505f2..a0373aa 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakamonModel.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/model/YakamonModel.java @@ -1,15 +1,20 @@ package fr.epita.assistants.yakamon.data.model; import jakarta.persistence.*; - import java.util.UUID; @Entity -@Table(name="yakamon") +@Table(name = "yakamon") public class YakamonModel { + @Id - @GeneratedValue(strategy = GenerationType.UUID) protected UUID id; + @GeneratedValue(strategy = GenerationType.UUID) + public UUID id; + public String nickname; - public Integer energyPoints; - @ManyToOne @JoinColumn(name="yakadex_id") public YakadexEntryModel yakadexEntryId; + public Integer energy_points; + + @ManyToOne + @JoinColumn(name = "yakadex_id") + public YakadexEntryModel yakadex_entry_id; } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/GameRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/GameRepository.java index 0f81567..53f6a1d 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/GameRepository.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/GameRepository.java @@ -1,3 +1,8 @@ package fr.epita.assistants.yakamon.data.repository; -public class GameRepository {} +import fr.epita.assistants.yakamon.data.model.GameModel; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class GameRepository implements PanacheRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/ItemRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/ItemRepository.java new file mode 100644 index 0000000..e1ef9ef --- /dev/null +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/ItemRepository.java @@ -0,0 +1,8 @@ +package fr.epita.assistants.yakamon.data.repository; + +import fr.epita.assistants.yakamon.data.model.ItemModel; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class ItemRepository implements PanacheRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/PlayerRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/PlayerRepository.java index 8b12f13..296a11b 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/PlayerRepository.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/PlayerRepository.java @@ -1,3 +1,8 @@ package fr.epita.assistants.yakamon.data.repository; -public class PlayerRepository {} +import fr.epita.assistants.yakamon.data.model.PlayerModel; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class PlayerRepository implements PanacheRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/TeamRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/TeamRepository.java deleted file mode 100644 index f45b646..0000000 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/TeamRepository.java +++ /dev/null @@ -1,3 +0,0 @@ -package fr.epita.assistants.yakamon.data.repository; - -public class TeamRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakadexRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakadexRepository.java index 26a32fe..ba2a4dc 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakadexRepository.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakadexRepository.java @@ -1,3 +1,9 @@ package fr.epita.assistants.yakamon.data.repository; -public class YakadexRepository {} +import fr.epita.assistants.yakamon.data.model.YakadexEntryModel; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class YakadexRepository + implements PanacheRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakamonRepository.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakamonRepository.java new file mode 100644 index 0000000..b38cf32 --- /dev/null +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/data/repository/YakamonRepository.java @@ -0,0 +1,8 @@ +package fr.epita.assistants.yakamon.data.repository; + +import fr.epita.assistants.yakamon.data.model.YakamonModel; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class YakamonRepository implements PanacheRepository {} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/GameEntity.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/GameEntity.java index 75bb0e9..8d327a9 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/GameEntity.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/GameEntity.java @@ -4,35 +4,37 @@ import java.time.LocalDateTime; public class GameEntity { - public Long id; + public static GameEntity INSTANCE = null; public String mapPath; public boolean started; public LocalDateTime startTime; + public TileEntity[][] map; - public GameEntity() {} + public GameEntity() { + this.start(null); + } - public GameEntity( - Long id, - String mapPath, - boolean started, - LocalDateTime startTime - ) { - this.id = id; + public GameEntity(String mapPath) { + this.start(null); this.mapPath = mapPath; - this.started = started; - this.startTime = startTime; + GameEntity.INSTANCE = this; } public GameEntity start(LocalDateTime startTime) { - // TODO + if (started || INSTANCE != null) { + this.stop(); + } startTime = startTime == null ? LocalDateTime.now() : startTime; - started = true; + this.startTime = startTime; + this.started = true; return this; } public GameEntity stop() { + GameEntity.INSTANCE = null; this.started = false; this.startTime = null; + this.map = null; return this; } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/ItemEntity.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/ItemEntity.java index 5114916..97f8f22 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/ItemEntity.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/ItemEntity.java @@ -2,26 +2,19 @@ package fr.epita.assistants.yakamon.domain.entity; import fr.epita.assistants.yakamon.utils.tile.ItemType; import java.util.Objects; +import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @NoArgsConstructor +@AllArgsConstructor public class ItemEntity { public Integer id; public ItemType type; public Integer quantity; - public ItemEntity(Integer id, ItemType type, Integer quantity) { - this.id = id; - if (type == null) { - System.err.println( - "Warning: attempted to pass null item type, using default 'NONE'" - ); - type = ItemType.NONE; - } - this.type = Objects.requireNonNull(type, "type must not be null"); - this.quantity = quantity != null ? quantity : 0; - } + // public ItemEntity(Integer id, ItemType type, Integer quantity) { + // } // public ItemEntity addQuantity(int amount) { // int current = this.quantity != null ? this.quantity : 0; diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/PlayerEntity.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/PlayerEntity.java new file mode 100644 index 0000000..7788d6d --- /dev/null +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/PlayerEntity.java @@ -0,0 +1,14 @@ +package fr.epita.assistants.yakamon.domain.entity; + +import java.time.LocalDateTime; + +public class PlayerEntity { + + public String name; + public Integer posX; + public Integer posY; + public LocalDateTime lastMove; + public LocalDateTime lastCatch; + public LocalDateTime lastCollect; + public LocalDateTime lastFeed; +} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/TileEntity.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/TileEntity.java new file mode 100644 index 0000000..6f110ae --- /dev/null +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/entity/TileEntity.java @@ -0,0 +1,16 @@ +package fr.epita.assistants.yakamon.domain.entity; + +public class TileEntity { + + public Character terrain; + public Character collectible; + + public TileEntity(Character terrain, Character collectible) { + this.terrain = terrain; + this.collectible = collectible; + } + + public boolean isWalkable() { + return terrain == 'G' || terrain == 'R' || terrain == 'S'; + } +} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/GameService.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/GameService.java index 4f0fc8b..0d4fd1c 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/GameService.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/GameService.java @@ -1,26 +1,72 @@ package fr.epita.assistants.yakamon.domain.service; +import fr.epita.assistants.yakamon.converter.EntityToModelConverter; +import fr.epita.assistants.yakamon.data.model.GameModel; +import fr.epita.assistants.yakamon.data.model.ItemModel; +import fr.epita.assistants.yakamon.data.model.PlayerModel; +import fr.epita.assistants.yakamon.data.repository.GameRepository; +import fr.epita.assistants.yakamon.data.repository.ItemRepository; +import fr.epita.assistants.yakamon.data.repository.PlayerRepository; import fr.epita.assistants.yakamon.domain.entity.GameEntity; +import fr.epita.assistants.yakamon.domain.entity.TileEntity; +import fr.epita.assistants.yakamon.utils.ErrorCode; +import fr.epita.assistants.yakamon.utils.tile.ItemType; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.time.LocalDateTime; -import lombok.NoArgsConstructor; +import java.util.ArrayList; +import java.util.List; -@NoArgsConstructor +@ApplicationScoped public class GameService { - public static GameEntity game = new GameEntity(); - public static final GameService INSTANCE = new GameService(); // TODO ? + @Inject + GameRepository gameRepository; - public GameEntity startGame(String mapPath) { + @Inject + PlayerRepository playerRepository; + + @Inject + ItemRepository itemRepository; + + public static GameEntity game; + + public GameEntity startGame(String mapPath, String playerName) { if (mapPath == null) { - throw new RuntimeException( - "Could not start game with null mapPath" - ); - } - if (game == null) { - game = new GameEntity(); + ErrorCode.INVALID_MAP.throwException("Map is null"); } - game.start(LocalDateTime.now()); + // Initialize Game with mapPath + game = new GameEntity(mapPath); + GameModel gameModel = EntityToModelConverter.toGameModel(game); + + // Parse map and store in GameEntity + TileEntity[][] parsedMap = parseMapFile(mapPath); + game.map = parsedMap; + + // Initialize player + LocalDateTime now = LocalDateTime.now(); + PlayerModel player = new PlayerModel(); + player.name = playerName; + player.posX = 0; + player.posY = 0; + player.lastCatch = now; + player.lastCollect = now; + player.lastFeed = now; + player.lastMove = now; + + // Initialize player inventory + ItemModel yakaballs = new ItemModel(); + yakaballs.type = ItemType.YAKABALL; + yakaballs.quantity = 5; + + // Store + playerRepository.persist(player); + itemRepository.persist(yakaballs); + gameRepository.persist(gameModel); + return game; } @@ -30,8 +76,78 @@ public class GameService { public void stopGame() { if (game == null) { - game = new GameEntity(); + ErrorCode.GAME_NOT_STARTED.throwException(); } game.stop(); + gameRepository.deleteAll(); + playerRepository.deleteAll(); + } + + private TileEntity[][] parseMapFile(String mapPath) { + try { + String resourcePath = "maps/" + mapPath + ".epimap"; + BufferedReader reader = new BufferedReader( + new InputStreamReader( + GameService.class.getClassLoader().getResourceAsStream( + resourcePath + ) + ) + ); + + List> mapList = new ArrayList<>(); + String line; + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty()) { + List row = parseLine(line); + mapList.add(row); + } + } + reader.close(); + + if (mapList.isEmpty()) { + ErrorCode.INVALID_MAP.throwException("Map file is empty"); + } + + TileEntity[][] map = new TileEntity[mapList.size()][]; + for (int i = 0; i < mapList.size(); i++) { + List row = mapList.get(i); + map[i] = row.toArray(new TileEntity[0]); + } + + return map; + } catch (Exception e) { + ErrorCode.INVALID_MAP.throwException("Failed to load map"); + } + return null; + } + + private List parseLine(String line) { + List row = new ArrayList<>(); + int i = 0; + + while (i < line.length()) { + // Parse count + int count = Character.getNumericValue(line.charAt(i)); + i++; + + // Parse terrain character + if (i >= line.length()) break; + char terrain = line.charAt(i); + i++; + + // Parse collectible character + if (i >= line.length()) break; + char collectible = line.charAt(i); + i++; + + // Add tiles + for (int j = 0; j < count; j++) { + row.add(new TileEntity(terrain, collectible)); + } + } + + return row; } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/PlayerService.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/PlayerService.java index 57edfb4..edc7d3e 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/PlayerService.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/PlayerService.java @@ -1,3 +1,240 @@ package fr.epita.assistants.yakamon.domain.service; -public class PlayerService {} +import fr.epita.assistants.yakamon.data.model.ItemModel; +import fr.epita.assistants.yakamon.data.model.PlayerModel; +import fr.epita.assistants.yakamon.data.model.YakadexEntryModel; +import fr.epita.assistants.yakamon.data.model.YakamonModel; +import fr.epita.assistants.yakamon.data.repository.ItemRepository; +import fr.epita.assistants.yakamon.data.repository.PlayerRepository; +import fr.epita.assistants.yakamon.data.repository.YakadexRepository; +import fr.epita.assistants.yakamon.data.repository.YakamonRepository; +import fr.epita.assistants.yakamon.domain.entity.GameEntity; +import fr.epita.assistants.yakamon.domain.entity.TileEntity; +import fr.epita.assistants.yakamon.utils.Direction; +import fr.epita.assistants.yakamon.utils.ErrorCode; +import fr.epita.assistants.yakamon.utils.Point; +import fr.epita.assistants.yakamon.utils.tile.Collectible; +import fr.epita.assistants.yakamon.utils.tile.CollectibleUtils; +import fr.epita.assistants.yakamon.utils.tile.ItemType; +import fr.epita.assistants.yakamon.utils.tile.YakamonInfo; +import fr.epita.assistants.yakamon.utils.tile.YakamonType; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.UUID; + +@ApplicationScoped +public class PlayerService { + + @Inject + PlayerRepository playerRepository; + + @Inject + YakamonRepository yakamonRepository; + + @Inject + YakadexRepository yakadexRepository; + + @Inject + ItemRepository itemRepository; + + @Transactional + public Point move(Direction direction) { + if (GameEntity.INSTANCE == null || !GameEntity.INSTANCE.started) { + ErrorCode.GAME_NOT_STARTED.throwException(); + } + + PlayerModel player = playerRepository.findAll().firstResult(); + if (player == null) { + ErrorCode.BAD_REQUEST.throwException("Player not found"); + } + + checkMovementDelay(player); + + if (direction == null) { + ErrorCode.BAD_REQUEST.throwException("Invalid direction"); + } + + TileEntity[][] map = GameEntity.INSTANCE.map; + if (map == null) { + ErrorCode.INVALID_MAP.throwException("Map not loaded"); + } + + Point directionOffset = direction.getPoint(); + int newX = player.posX + directionOffset.getPosX(); + int newY = player.posY + directionOffset.getPosY(); + + if ( + newY < 0 || + newY >= map.length || + newX < 0 || + newX >= map[newY].length + ) { + ErrorCode.BAD_REQUEST.throwException( + "Cannot move outside map boundaries" + ); + } + + TileEntity tile = map[newY][newX]; + if (tile == null || !tile.isWalkable()) { + ErrorCode.BAD_REQUEST.throwException("Tile is not walkable"); + } + + player.posX = newX; + player.posY = newY; + player.lastMove = LocalDateTime.now(); + + return new Point(newX, newY); + } + + @Transactional + public YakamonModel catchYakamon() { + if (GameEntity.INSTANCE == null || !GameEntity.INSTANCE.started) { + ErrorCode.GAME_NOT_STARTED.throwException(); + } + + PlayerModel player = playerRepository.findAll().firstResult(); + if (player == null) { + ErrorCode.BAD_REQUEST.throwException("Player not found"); + } + + checkCatchDelay(player); + + TileEntity[][] map = GameEntity.INSTANCE.map; + if (map == null) { + ErrorCode.INVALID_MAP.throwException("Map not loaded"); + } + + int x = player.posX; + int y = player.posY; + + if (y < 0 || y >= map.length || x < 0 || x >= map[y].length) { + ErrorCode.BAD_REQUEST.throwException("Invalid position"); + } + + TileEntity tile = map[y][x]; + if ( + tile == null || tile.collectible == null || tile.collectible == 'N' + ) { + throw new WebApplicationException( + Response.status(400) + .entity("No yakamon at this position") + .build() + ); + } + + Collectible collectible = CollectibleUtils.getCollectible( + tile.collectible + ); + if (!(collectible instanceof YakamonType)) { + throw new WebApplicationException( + Response.status(400) + .entity("No yakamon at this position") + .build() + ); + } + + ItemModel yakaballItem = itemRepository + .find("type", ItemType.YAKABALL) + .firstResult(); + if (yakaballItem == null || yakaballItem.quantity < 1) { + throw new WebApplicationException( + Response.status(400).entity("Not enough Yakaballs").build() + ); + } + + long teamSize = yakamonRepository.count(); + if (teamSize >= 3) { + throw new WebApplicationException( + Response.status(400).entity("Team is full").build() + ); + } + + YakamonType yakamonType = (YakamonType) collectible; + YakamonInfo yakamonInfo = + (YakamonInfo) yakamonType.getCollectibleInfo(); + Integer yakadexId = yakamonInfo.getYakadexId(); + + YakadexEntryModel yakadex = yakadexRepository + .find("id", yakadexId) + .firstResult(); + if (yakadex == null) { + ErrorCode.BAD_REQUEST.throwException("Yakamon species not found"); + } + + yakaballItem.quantity -= 1; + itemRepository.persist(yakaballItem); + + YakamonModel newYakamon = new YakamonModel(); + newYakamon.nickname = yakamonType.name(); + newYakamon.energy_points = 0; + newYakamon.yakadex_entry_id = yakadex; + yakamonRepository.persist(newYakamon); + + yakadex.caught = true; + yakadexRepository.persist(yakadex); + + tile.collectible = 'N'; + + player.lastCatch = LocalDateTime.now(); + playerRepository.persist(player); + + return newYakamon; + } + + private void checkMovementDelay(PlayerModel player) { + String tickDurationStr = System.getenv("JWS_TICK_DURATION"); + String movementDelayStr = System.getenv("JWS_MOVEMENT_DELAY"); + + if (tickDurationStr == null || movementDelayStr == null) { + return; + } + + try { + long tickDuration = Long.parseLong(tickDurationStr); + long movementDelay = Long.parseLong(movementDelayStr); + long requiredDelay = tickDuration * movementDelay; + + LocalDateTime now = LocalDateTime.now(); + long elapsed = ChronoUnit.MILLIS.between(player.lastMove, now); + + if (elapsed < requiredDelay) { + throw new WebApplicationException(Response.status(429).build()); + } + } catch (NumberFormatException e) { + ErrorCode.BAD_REQUEST.throwException( + "Invalid environment variables" + ); + } + } + + private void checkCatchDelay(PlayerModel player) { + String tickDurationStr = System.getenv("JWS_TICK_DURATION"); + String catchDelayStr = System.getenv("JWS_CATCH_DELAY"); + + if (tickDurationStr == null || catchDelayStr == null) { + return; + } + + try { + long tickDuration = Long.parseLong(tickDurationStr); + long catchDelay = Long.parseLong(catchDelayStr); + long requiredDelay = tickDuration * catchDelay; + + LocalDateTime now = LocalDateTime.now(); + long elapsed = ChronoUnit.MILLIS.between(player.lastCatch, now); + + if (elapsed < requiredDelay) { + throw new WebApplicationException(Response.status(429).build()); + } + } catch (NumberFormatException e) { + ErrorCode.BAD_REQUEST.throwException( + "Invalid environment variables" + ); + } + } +} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/YakadexService.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/YakadexService.java index 97881ae..42285af 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/YakadexService.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/domain/service/YakadexService.java @@ -1,3 +1,66 @@ package fr.epita.assistants.yakamon.domain.service; -public class YakadexService {} +import fr.epita.assistants.yakamon.data.model.YakadexEntryModel; +import fr.epita.assistants.yakamon.data.repository.YakadexRepository; +import fr.epita.assistants.yakamon.presentation.api.YakadexEntry; +import fr.epita.assistants.yakamon.presentation.api.response.YakadexAllInfosResponse; +import fr.epita.assistants.yakamon.presentation.api.response.YakadexInfosResponse; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@ApplicationScoped +public class YakadexService { + + @Inject + YakadexRepository yakadexRepository; + + public YakadexAllInfosResponse getAllEntries(Boolean onlyMissing) { + List entries; + + if (onlyMissing != null && onlyMissing) { + entries = yakadexRepository.find("caught = false").list(); + } else { + entries = yakadexRepository.listAll(); + } + + List dtos = new ArrayList<>(); + for (YakadexEntryModel entry : entries) { + YakadexEntry dto = new YakadexInfosResponse( + entry.id, + entry.name, + entry.first_type, + entry.second_type, + entry.evolve_threshold, + null, + entry.caught, + entry.description + ); + dtos.add(dto); + } + + return new YakadexAllInfosResponse(dtos.toArray(new YakadexEntry[0])); + } + + public YakadexInfosResponse getEntryById(Integer id) { + YakadexEntryModel entry = yakadexRepository + .find("id = ?1", id) + .firstResult(); + + if (entry == null) { + return null; + } + + return new YakadexInfosResponse( + entry.id, + entry.name, + entry.first_type, + entry.second_type, + entry.evolve_threshold, + null, + entry.caught, + entry.description + ); + } +} diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/api/request/PlayerMoveRequest.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/api/request/PlayerMoveRequest.java index e5228b6..3e79362 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/api/request/PlayerMoveRequest.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/api/request/PlayerMoveRequest.java @@ -2,9 +2,13 @@ package fr.epita.assistants.yakamon.presentation.api.request; import fr.epita.assistants.yakamon.utils.Direction; import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data @AllArgsConstructor +@NoArgsConstructor public class PlayerMoveRequest { - Direction direction; + public Direction direction; } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/GameResource.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/GameResource.java index a35c84a..dd8218c 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/GameResource.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/GameResource.java @@ -40,23 +40,32 @@ public class GameResource { } LOGGER.info( - "Starting game for player='" + + "Starting game for player '" + request.playerName + - "' with map='" + + "' with map '" + request.mapPath + "'" ); - GameService gameService = new GameService(); - GameEntity startedGame = gameService.startGame(request.mapPath); + try { + GameService gameService = new GameService(); + GameEntity startedGame = gameService.startGame( + request.mapPath, + request.playerName + ); + LOGGER.info( + "Game started at: " + + (startedGame != null ? startedGame.startTime : "unknown") + ); - LOGGER.info( - "Game started at: " + - (startedGame != null ? startedGame.startTime : "unknown") - ); - - // StartGameResponse response = new StartGameResponse(); - // return Response.ok(response).build(); - return Response.ok().build(); + // StartGameResponse response = new StartGameResponse(); + // return Response.ok(response).build(); + return Response.ok().build(); + } catch (Exception e) { + return Response.status( + 400, + "Invalid path or invalid name provided." + ).build(); + } } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/InventoryResource.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/InventoryResource.java index 6aab3fc..757b215 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/InventoryResource.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/InventoryResource.java @@ -1,5 +1,6 @@ package fr.epita.assistants.yakamon.presentation.rest; +import fr.epita.assistants.yakamon.domain.service.GameService; import fr.epita.assistants.yakamon.presentation.api.response.GetInventoryResponse; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; @@ -13,10 +14,18 @@ import jakarta.ws.rs.core.Response; @Consumes(MediaType.APPLICATION_JSON) public class InventoryResource { + private final GameService gameService = new GameService(); + @GET public Response getInventory() { - // GetInventoryResponse response = new GetInventoryResponse(); + if (!gameService.isStarted()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Game not started") + .build(); + } + // TODO service + // GetInventoryResponse response = inventoryService.getInventory(); // return Response.ok(response).build(); - return Response.ok().build(); + return Response.status(Response.Status.NOT_IMPLEMENTED).build(); } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/PlayerResource.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/PlayerResource.java index a9f76b3..2ac486c 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/PlayerResource.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/PlayerResource.java @@ -1,12 +1,14 @@ package fr.epita.assistants.yakamon.presentation.rest; import fr.epita.assistants.yakamon.domain.service.GameService; +import fr.epita.assistants.yakamon.domain.service.PlayerService; import fr.epita.assistants.yakamon.presentation.api.request.PlayerMoveRequest; import fr.epita.assistants.yakamon.presentation.api.response.GetInventoryResponse; import fr.epita.assistants.yakamon.presentation.api.response.PlayerCatchResponse; import fr.epita.assistants.yakamon.presentation.api.response.PlayerCollectResponse; import fr.epita.assistants.yakamon.presentation.api.response.PlayerInfosResponse; import fr.epita.assistants.yakamon.presentation.api.response.PlayerMoveResponse; +import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -16,7 +18,11 @@ import jakarta.ws.rs.core.Response; @Produces(MediaType.APPLICATION_JSON) public class PlayerResource { - private final GameService gameService = new GameService(); + @Inject + private GameService gameService; + + @Inject + private PlayerService playerService; @POST @Path("/catch") @@ -26,8 +32,23 @@ public class PlayerResource { .entity("Game not started") .build(); } - // TODO service - return Response.status(Response.Status.NOT_IMPLEMENTED).build(); + + try { + var yakamon = playerService.catchYakamon(); + var response = new PlayerCatchResponse( + yakamon.id.toString(), + yakamon.nickname, + yakamon.yakadex_entry_id.id, + yakamon.energy_points + ); + return Response.ok(response).build(); + } catch (jakarta.ws.rs.WebApplicationException e) { + return e.getResponse(); + } catch (Exception e) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(e.getMessage()) + .build(); + } } @POST @@ -42,20 +63,6 @@ public class PlayerResource { return Response.status(Response.Status.NOT_IMPLEMENTED).build(); } - @GET - @Path("/inventory") - public Response getInventory() { - if (!gameService.isStarted()) { - return Response.status(Response.Status.BAD_REQUEST) - .entity("Game not started") - .build(); - } - // TODO service - // GetInventoryResponse response = inventoryService.getInventory(); - // return Response.ok(response).build(); - return Response.status(Response.Status.NOT_IMPLEMENTED).build(); - } - @POST @Path("/move") public Response move(PlayerMoveRequest request) { @@ -64,19 +71,27 @@ public class PlayerResource { .entity("Game not started") .build(); } - if (request == null) { + + if (request == null || request.direction == null) { return Response.status(Response.Status.BAD_REQUEST) - .entity("Request body is required") + .entity("Invalid direction") .build(); } - // if (request.direction == null) { - // return Response.status(Response.Status.BAD_REQUEST) - // .entity("Direction is required") - // .build(); - // } - // TODO service - return Response.status(Response.Status.NOT_IMPLEMENTED).build(); + try { + fr.epita.assistants.yakamon.utils.Point newPos = playerService.move( + request.direction + ); + return Response.ok( + new PlayerMoveResponse(newPos.getPosX(), newPos.getPosY()) + ).build(); + } catch (jakarta.ws.rs.WebApplicationException e) { + return e.getResponse(); + } catch (Exception e) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(e.getMessage()) + .build(); + } } @GET diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/YakadexResource.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/YakadexResource.java index d3e51dd..54c7102 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/YakadexResource.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/presentation/rest/YakadexResource.java @@ -1,8 +1,10 @@ package fr.epita.assistants.yakamon.presentation.rest; import fr.epita.assistants.yakamon.domain.service.GameService; +import fr.epita.assistants.yakamon.domain.service.YakadexService; import fr.epita.assistants.yakamon.presentation.api.response.YakadexAllInfosResponse; import fr.epita.assistants.yakamon.presentation.api.response.YakadexInfosResponse; +import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -12,7 +14,11 @@ import jakarta.ws.rs.core.Response; @Consumes(MediaType.APPLICATION_JSON) public class YakadexResource { - private final GameService gameService = new GameService(); + @Inject + private GameService gameService; + + @Inject + private YakadexService yakadexService; @GET public Response getYakadex( @@ -24,9 +30,10 @@ public class YakadexResource { .build(); } - // YakadexAllInfosResponse response = new YakadexAllInfosResponse(); - // return Response.ok(response).build(); - return Response.ok().build(); + YakadexAllInfosResponse response = yakadexService.getAllEntries( + onlyMissing + ); + return Response.ok(response).build(); } @GET @@ -38,12 +45,12 @@ public class YakadexResource { .build(); } - if (id == null || id < 0) { + YakadexInfosResponse response = yakadexService.getEntryById(id); + + if (response == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - // YakadexInfosResponse response = new YakadexInfosResponse(); - // return Response.ok(response).build(); - return Response.ok().build(); + return Response.ok(response).build(); } } diff --git a/yakamon/src/main/java/fr/epita/assistants/yakamon/utils/ErrorCode.java b/yakamon/src/main/java/fr/epita/assistants/yakamon/utils/ErrorCode.java index 5d89ce8..db0f9bf 100644 --- a/yakamon/src/main/java/fr/epita/assistants/yakamon/utils/ErrorCode.java +++ b/yakamon/src/main/java/fr/epita/assistants/yakamon/utils/ErrorCode.java @@ -1,23 +1,31 @@ package fr.epita.assistants.yakamon.utils; +import static jakarta.ws.rs.core.Response.Status; + import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; import lombok.Getter; import lombok.RequiredArgsConstructor; -import static jakarta.ws.rs.core.Response.Status; - @Getter @RequiredArgsConstructor public enum ErrorCode { - EXAMPLE_ERROR(Status.INTERNAL_SERVER_ERROR, "This is an error example"); + EXAMPLE_ERROR(Status.INTERNAL_SERVER_ERROR, "This is an error example"), + BAD_REQUEST(Status.BAD_REQUEST, "Bad request"), + INVALID_MAP(Status.BAD_REQUEST, "Given map is invalid"), + GAME_NOT_STARTED(Status.CONFLICT, "Game has not been started"), + GAME_ALREADY_STARTED(Status.FORBIDDEN, "Game is already started"); private final Response.Status errorCode; private final String errorMessage; public WebApplicationException getException() { - return new WebApplicationException(Response.status(errorCode).entity(new ErrorInfo(errorMessage)).build()); + return new WebApplicationException( + Response.status(errorCode) + .entity(new ErrorInfo(errorMessage)) + .build() + ); } public void throwException() { @@ -25,6 +33,10 @@ public enum ErrorCode { } public void throwException(String prefix) { - throw new WebApplicationException(Response.status(errorCode).entity(new ErrorInfo(prefix + ": " + errorMessage)).build()); + throw new WebApplicationException( + Response.status(errorCode) + .entity(new ErrorInfo(prefix + ": " + errorMessage)) + .build() + ); } } diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ConverterTests.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ConverterTests.java new file mode 100644 index 0000000..c2d80d2 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ConverterTests.java @@ -0,0 +1,239 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static org.junit.jupiter.api.Assertions.*; + +import fr.epita.assistants.yakamon.converter.DtoToEntityConverter; +import fr.epita.assistants.yakamon.converter.EntityToDtoConverter; +import fr.epita.assistants.yakamon.converter.EntityToModelConverter; +import fr.epita.assistants.yakamon.domain.entity.GameEntity; +import fr.epita.assistants.yakamon.domain.entity.YakadexEntryEntity; +import fr.epita.assistants.yakamon.domain.entity.YakamonEntity; +import fr.epita.assistants.yakamon.presentation.api.YakadexEntry; +import fr.epita.assistants.yakamon.presentation.api.YakamonAction; +import fr.epita.assistants.yakamon.presentation.api.request.StartGameRequest; +import fr.epita.assistants.yakamon.utils.ElementType; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ConverterTests") +public class ConverterTests { + + @Test + @DisplayName("ModelToYakamon: null returns null") + public void testNullYakamonToModel() { + assertNull(EntityToModelConverter.toYakamonModel(null)); + } + + @Test + @DisplayName("ModelToYakamon: valid conversion") + public void testYakamonToModel() { + YakamonEntity yakamon = new YakamonEntity(); + yakamon.uuid = UUID.randomUUID(); + yakamon.nickname = "Bastiedon"; + yakamon.yakadexId = 6; + yakamon.energyPoints = 50; + + var model = EntityToModelConverter.toYakamonModel(yakamon); + + assertNotNull(model); + assertEquals("Bastiedon", model.nickname); + assertEquals(50, model.energy_points); + } + + @Test + @DisplayName("ModelToYakadex: null returns null") + public void testNullYakadexToModel() { + assertNull(EntityToModelConverter.toYakadexEntryModel(null)); + } + + @Test + @DisplayName("ModelToYakadex: single type") + public void testYakadexSingleType() { + YakadexEntryEntity yakadex = new YakadexEntryEntity(); + yakadex.id = 1; + yakadex.name = "Yakimon"; + yakadex.firstType = ElementType.NORMAL; + yakadex.secondType = null; + yakadex.caught = true; + + var model = EntityToModelConverter.toYakadexEntryModel(yakadex); + + assertNotNull(model); + assertEquals("NORMAL", model.first_type); + assertNull(model.second_type); + } + + @Test + @DisplayName("ModelToYakadex: dual types") + public void testYakadexDualTypes() { + YakadexEntryEntity yakadex = new YakadexEntryEntity(); + yakadex.id = 25; + yakadex.name = "Pikachu"; + yakadex.firstType = ElementType.ELECTRIC; + yakadex.secondType = ElementType.FLYING; + yakadex.caught = false; + + var model = EntityToModelConverter.toYakadexEntryModel(yakadex); + + assertNotNull(model); + assertEquals("ELECTRIC", model.first_type); + assertEquals("FLYING", model.second_type); + } + + @Test + @DisplayName("DtoToYakamon: null returns null") + public void testNullYakamonAction() { + assertNull(DtoToEntityConverter.toYakamonEntity(null)); + } + + @Test + @DisplayName("DtoToYakamon: valid conversion") + public void testYakamonActionValid() { + UUID uuid = UUID.randomUUID(); + YakamonAction action = new YakamonAction( + uuid.toString(), + "Bastiedon", + 6, + 100 + ); + + YakamonEntity entity = DtoToEntityConverter.toYakamonEntity(action); + + assertNotNull(entity); + assertEquals(uuid, entity.uuid); + assertEquals("Bastiedon", entity.nickname); + } + + @Test + @DisplayName("DtoToYakamon: invalid uuid handled") + public void testYakamonInvalidUuid() { + YakamonAction action = new YakamonAction("not-a-uuid", "Test", 1, 0); + YakamonEntity entity = DtoToEntityConverter.toYakamonEntity(action); + + assertNotNull(entity); + assertNull(entity.uuid); + } + + @Test + @DisplayName("DtoToYakadex: null returns null") + public void testNullYakadexEntry() { + assertNull(DtoToEntityConverter.toYakadexEntryEntity(null)); + } + + @Test + @DisplayName("DtoToYakadex: valid types") + public void testYakadexTypesConvert() { + YakadexEntry dto = new YakadexEntry( + 1, + "Yakimon", + "NORMAL", + "FLYING", + 10, + 2, + true, + "A creature" + ); + YakadexEntryEntity entity = DtoToEntityConverter.toYakadexEntryEntity( + dto + ); + + assertNotNull(entity); + assertEquals(ElementType.NORMAL, entity.firstType); + assertEquals(ElementType.FLYING, entity.secondType); + } + + @Test + @DisplayName("DtoToYakadex: invalid enum handled") + public void testYakadexInvalidEnum() { + YakadexEntry dto = new YakadexEntry( + 1, + "Test", + "INVALID", + null, + 10, + null, + false, + "Test" + ); + YakadexEntryEntity entity = DtoToEntityConverter.toYakadexEntryEntity( + dto + ); + + assertNotNull(entity); + assertNull(entity.firstType); + } + + @Test + @DisplayName("DtoToGame: converts correctly") + public void testGameDtoConvert() { + StartGameRequest request = new StartGameRequest("test_map", "Player"); + GameEntity entity = DtoToEntityConverter.toGameEntity(request); + + assertNotNull(entity); + assertEquals("test_map", entity.mapPath); + assertTrue(entity.started); + } + + @Test + @DisplayName("EntityToYakamon: null returns null") + public void testNullYakamonDto() { + assertNull(EntityToDtoConverter.toYakamonAction(null)); + } + + @Test + @DisplayName("EntityToYakamon: valid conversion") + public void testYakamonDto() { + YakamonEntity yakamon = new YakamonEntity(); + yakamon.uuid = UUID.randomUUID(); + yakamon.nickname = "Bastiedon"; + yakamon.yakadexId = 6; + yakamon.energyPoints = 75; + + YakamonAction action = EntityToDtoConverter.toYakamonAction(yakamon); + + assertNotNull(action); + assertEquals("Bastiedon", action.nickname); + assertEquals(6, action.yakadexId); + } + + @Test + @DisplayName("EntityToYakadex: null returns null") + public void testNullYakadexDto() { + assertNull(EntityToDtoConverter.toYakadexEntry(null)); + } + + @Test + @DisplayName("EntityToYakadex: conversion") + public void testYakadexDto() { + YakadexEntryEntity yakadex = new YakadexEntryEntity(); + yakadex.id = 1; + yakadex.name = "Yakimon"; + yakadex.firstType = ElementType.NORMAL; + yakadex.secondType = null; + yakadex.caught = true; + + YakadexEntry entry = EntityToDtoConverter.toYakadexEntry(yakadex); + + assertNotNull(entry); + assertEquals("NORMAL", entry.firstType); + assertNull(entry.secondType); + } + + @Test + @DisplayName("EntityToYakamon: list conversion") + public void testYakamonListDto() { + YakamonEntity y1 = new YakamonEntity(); + y1.uuid = UUID.randomUUID(); + y1.nickname = "Yak1"; + y1.yakadexId = 1; + y1.energyPoints = 0; + + var result = EntityToDtoConverter.toYakamonActions( + java.util.List.of(y1) + ); + + assertEquals(1, result.size()); + assertEquals("Yak1", result.get(0).nickname); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/GameResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/GameResourceTest.java new file mode 100644 index 0000000..148f4a6 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/GameResourceTest.java @@ -0,0 +1,189 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("GameResourceTest") +public class GameResourceTest { + + @Test + @DisplayName("POST /start/ with valid request") + public void testStartGameWithValidRequest() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /start without trailing slash") + public void testStartGameWithoutTrailingSlash() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /start/ missing mapPath returns 400") + public void testStartGameWithoutMapPath() { + String requestBody = "{\"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ missing playerName returns 400") + public void testStartGameWithoutPlayerName() { + String requestBody = "{\"mapPath\": \"map1.txt\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ blank mapPath returns 400") + public void testStartGameWithBlankMapPath() { + String requestBody = "{\"mapPath\": \"\", \"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ blank playerName returns 400") + public void testStartGameWithBlankPlayerName() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ both fields blank returns 400") + public void testStartGameWithBothBlank() { + String requestBody = "{\"mapPath\": \"\", \"playerName\": \"\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ whitespace mapPath returns 400") + public void testStartGameWithWhitespaceMapPath() { + String requestBody = + "{\"mapPath\": \" \", \"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ whitespace playerName returns 400") + public void testStartGameWithWhitespacePlayerName() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \" \"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /start/ with special characters in name") + public void testStartGameWithSpecialCharacterName() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"Alice-Smith_123\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /start/ with complex map path") + public void testStartGameWithComplexMapPath() { + String requestBody = + "{\"mapPath\": \"maps/world/level1/forest.txt\", \"playerName\": \"TestPlayer\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /start/ returns JSON") + public void testStartGameContentType() { + String requestBody = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"Alice\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/start/") + .then() + .contentType(ContentType.JSON); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/HelloWorldResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/HelloWorldResourceTest.java new file mode 100644 index 0000000..ff72850 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/HelloWorldResourceTest.java @@ -0,0 +1,79 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("HelloWorldResourceTest") +public class HelloWorldResourceTest { + + @Test + @DisplayName("GET /hello/") + public void testHelloWorldWithoutName() { + given() + .when() + .get("/hello/") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } + + @Test + @DisplayName("GET /hello without trailing slash") + public void testHelloWorldWithoutTrailingSlash() { + given() + .when() + .get("/hello") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } + + @Test + @DisplayName("GET /hello/{name}") + public void testHelloWorldWithName() { + given() + .when() + .get("/hello/Alice") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } + + @Test + @DisplayName("GET /hello/{name} with different name") + public void testHelloWorldWithDifferentName() { + given() + .when() + .get("/hello/Bob") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } + + @Test + @DisplayName("GET /hello/{name} with special characters") + public void testHelloWorldWithSpecialCharacters() { + given() + .when() + .get("/hello/John-Doe") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } + + @Test + @DisplayName("GET /hello/{name} with numeric name") + public void testHelloWorldWithNumericName() { + given() + .when() + .get("/hello/123") + .then() + .statusCode(200) + .contentType(ContentType.TEXT); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/InventoryResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/InventoryResourceTest.java new file mode 100644 index 0000000..4eac668 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/InventoryResourceTest.java @@ -0,0 +1,116 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("InventoryResourceTest") +public class InventoryResourceTest { + + @Test + @DisplayName("GET /inventory returns 200") + public void testGetInventorySuccess() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /inventory returns JSON") + public void testGetInventoryContentType() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /inventory without trailing slash") + public void testGetInventoryWithoutTrailingSlash() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200) + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /inventory multiple requests") + public void testGetInventoryMultipleCalls() { + for (int i = 0; i < 5; i++) { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200) + .contentType(ContentType.JSON); + } + } + + @Test + @DisplayName("GET /inventory with accept header") + public void testGetInventoryWithAcceptHeader() { + given() + .accept(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200) + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /inventory without body") + public void testGetInventoryNoBody() { + given().when().get("/inventory").then().statusCode(200); + } + + @Test + @DisplayName("GET /inventory with query params") + public void testGetInventoryWithQueryParams() { + given() + .contentType(ContentType.JSON) + .queryParam("filter", "all") + .when() + .get("/inventory") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /inventory rapid consecutive requests") + public void testGetInventoryRapidRequests() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200); + + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200); + + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(200); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/MainTests.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/MainTests.java new file mode 100644 index 0000000..64f5f98 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/MainTests.java @@ -0,0 +1,7 @@ +package fr.epita.assistants.yakamon_testsuite; + +// import static org.junit.jupiter.api.Assertions.*; + +// import org.junit.jupiter.api.Test; + +public class MainTests {} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/PlayerResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/PlayerResourceTest.java new file mode 100644 index 0000000..1e82eb0 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/PlayerResourceTest.java @@ -0,0 +1,210 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("PlayerResourceTest") +public class PlayerResourceTest { + + @Test + @DisplayName("POST /catch without game returns 400") + public void testCatchYakamonWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .post("/catch") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /collect without game returns 400") + public void testCollectWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .post("/collect") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /inventory without game returns 400") + public void testGetInventoryWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move without game returns 400") + public void testMoveWithoutGameStarted() { + String requestBody = "{\"direction\": \"UP\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move without body returns 400") + public void testMoveWithoutRequestBody() { + given() + .contentType(ContentType.JSON) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /player without game returns 400") + public void testGetPlayerInfosWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .get("/player") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /inventory returns JSON") + public void testGetInventoryContentType() { + given() + .contentType(ContentType.JSON) + .when() + .get("/inventory") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /move returns JSON") + public void testMoveContentType() { + String requestBody = "{\"direction\": \"UP\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /player returns JSON") + public void testGetPlayerContentType() { + given() + .contentType(ContentType.JSON) + .when() + .get("/player") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /catch returns JSON") + public void testCatchContentType() { + given() + .contentType(ContentType.JSON) + .when() + .post("/catch") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /collect returns JSON") + public void testCollectContentType() { + given() + .contentType(ContentType.JSON) + .when() + .post("/collect") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /move with UP direction") + public void testMoveWithUpDirection() { + String requestBody = "{\"direction\": \"UP\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move with DOWN direction") + public void testMoveWithDownDirection() { + String requestBody = "{\"direction\": \"DOWN\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move with LEFT direction") + public void testMoveWithLeftDirection() { + String requestBody = "{\"direction\": \"LEFT\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move with RIGHT direction") + public void testMoveWithRightDirection() { + String requestBody = "{\"direction\": \"RIGHT\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /move with invalid direction") + public void testMoveWithInvalidDirection() { + String requestBody = "{\"direction\": \"INVALID\"}"; + + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/move") + .then() + .statusCode(400); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ServiceLayerTests.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ServiceLayerTests.java new file mode 100644 index 0000000..be22cf8 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/ServiceLayerTests.java @@ -0,0 +1,173 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static org.junit.jupiter.api.Assertions.*; + +import fr.epita.assistants.yakamon.domain.entity.GameEntity; +import fr.epita.assistants.yakamon.domain.entity.TileEntity; +import fr.epita.assistants.yakamon.utils.Direction; +import fr.epita.assistants.yakamon.utils.Point; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("ServiceLayerTests") +public class ServiceLayerTests { + + @BeforeEach + public void setup() { + GameEntity.INSTANCE = null; + } + + @Test + @DisplayName("GameStart") + public void testGameStart() { + GameEntity game = new GameEntity("test_map"); + assertTrue(game.started); + assertEquals("test_map", game.mapPath); + } + + @Test + @DisplayName("GameStop") + public void testGameStop() { + GameEntity game = new GameEntity("map"); + game.stop(); + assertFalse(game.started); + assertNull(GameEntity.INSTANCE); + } + + @Test + @DisplayName("GameSetMap") + public void testGameSetMap() { + GameEntity game = new GameEntity("map"); + TileEntity[][] map = new TileEntity[2][2]; + game.map = map; + assertNotNull(game.map); + assertEquals(2, game.map.length); + } + + @Test + @DisplayName("TileGrassWalkable") + public void testGrassWalkable() { + TileEntity tile = new TileEntity('G', 'N'); + assertTrue(tile.isWalkable()); + } + + @Test + @DisplayName("TileSandWalkable") + public void testSandWalkable() { + TileEntity tile = new TileEntity('S', 'N'); + assertTrue(tile.isWalkable()); + } + + @Test + @DisplayName("TileRockWalkable") + public void testRockWalkable() { + TileEntity tile = new TileEntity('R', 'N'); + assertTrue(tile.isWalkable()); + } + + @Test + @DisplayName("TileWaterNotWalkable") + public void testWaterNotWalkable() { + TileEntity tile = new TileEntity('W', 'N'); + assertFalse(tile.isWalkable()); + } + + @Test + @DisplayName("TileMountainNotWalkable") + public void testMountainNotWalkable() { + TileEntity tile = new TileEntity('M', 'N'); + assertFalse(tile.isWalkable()); + } + + @Test + @DisplayName("TileLavaNotWalkable") + public void testLavaNotWalkable() { + TileEntity tile = new TileEntity('L', 'N'); + assertFalse(tile.isWalkable()); + } + + @Test + @DisplayName("TileCollectible") + public void testTileCollectible() { + TileEntity tile = new TileEntity('G', 'Y'); + assertEquals('Y', tile.collectible); + assertEquals('G', tile.terrain); + } + + @Test + @DisplayName("DirectionUp") + public void testDirectionUp() { + Point p = Direction.UP.getPoint(); + assertEquals(0, p.getPosX()); + assertEquals(-1, p.getPosY()); + } + + @Test + @DisplayName("DirectionDown") + public void testDirectionDown() { + Point p = Direction.DOWN.getPoint(); + assertEquals(0, p.getPosX()); + assertEquals(1, p.getPosY()); + } + + @Test + @DisplayName("DirectionLeft") + public void testDirectionLeft() { + Point p = Direction.LEFT.getPoint(); + assertEquals(-1, p.getPosX()); + assertEquals(0, p.getPosY()); + } + + @Test + @DisplayName("DirectionRight") + public void testDirectionRight() { + Point p = Direction.RIGHT.getPoint(); + assertEquals(1, p.getPosX()); + assertEquals(0, p.getPosY()); + } + + @Test + @DisplayName("PointCreation") + public void testPointCreation() { + Point p = new Point(5, 10); + assertEquals(5, p.getPosX()); + assertEquals(10, p.getPosY()); + } + + @Test + @DisplayName("GameInstance") + public void testGameInstance() { + GameEntity g1 = new GameEntity("map1"); + assertSame(GameEntity.INSTANCE, g1); + } + + @Test + @DisplayName("MapGrid") + public void testMapGrid() { + TileEntity[][] map = new TileEntity[3][3]; + map[0][0] = new TileEntity('G', 'N'); + map[1][1] = new TileEntity('W', 'N'); + map[2][2] = new TileEntity('R', 'Y'); + + assertTrue(map[0][0].isWalkable()); + assertFalse(map[1][1].isWalkable()); + assertTrue(map[2][2].isWalkable()); + } + + @Test + @DisplayName("YakamonTile") + public void testYakamonTile() { + TileEntity tile = new TileEntity('G', 'b'); + assertEquals('b', tile.collectible); + assertTrue(tile.isWalkable()); + } + + @Test + @DisplayName("ItemTile") + public void testItemTile() { + TileEntity tile = new TileEntity('S', 'S'); + assertEquals('S', tile.collectible); + assertTrue(tile.isWalkable()); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/TeamResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/TeamResourceTest.java new file mode 100644 index 0000000..ba60b94 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/TeamResourceTest.java @@ -0,0 +1,457 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("TeamResourceTest") +public class TeamResourceTest { + + private static final String VALID_UUID = + "550e8400-e29b-41d4-a716-446655440000"; + private static final String ANOTHER_UUID = + "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; + + @Test + @DisplayName("GET /team without game returns 400") + public void testGetTeamWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .get("/team") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /team returns JSON") + public void testGetTeamContentType() { + given() + .contentType(ContentType.JSON) + .when() + .get("/team") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /team/{uuid}/evolve without game returns 400") + public void testEvolveWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .post("/team/" + VALID_UUID + "/evolve") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/evolve with blank uuid returns 400") + public void testEvolveWithBlankUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .post("/team//evolve") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/evolve with valid uuid returns 200") + public void testEvolveWithValidUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .post("/team/" + VALID_UUID + "/evolve") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /team/{uuid}/evolve returns JSON") + public void testEvolveContentType() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .post("/team/" + VALID_UUID + "/evolve") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("POST /team/{uuid}/feed without game returns 400") + public void testFeedWithoutGameStarted() { + String requestBody = "{\"quantity\": 10}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed with blank uuid returns 400") + public void testFeedWithBlankUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": 10}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team//feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed with null quantity returns 400") + public void testFeedWithNullQuantity() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": null}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed with zero quantity returns 400") + public void testFeedWithZeroQuantity() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": 0}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed with negative quantity returns 400") + public void testFeedWithNegativeQuantity() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": -5}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed with valid quantity returns 200") + public void testFeedWithValidQuantity() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": 10}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(200); + } + + @Test + @DisplayName("POST /team/{uuid}/feed without body returns 400") + public void testFeedWithMissingRequestBody() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .statusCode(400); + } + + @Test + @DisplayName("POST /team/{uuid}/feed returns JSON") + public void testFeedContentType() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"quantity\": 10}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .post("/team/" + VALID_UUID + "/feed") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("DELETE /team/{uuid}/release without game returns 400") + public void testReleaseWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .delete("/team/" + VALID_UUID + "/release") + .then() + .statusCode(400); + } + + @Test + @DisplayName("DELETE /team/{uuid}/release with blank uuid returns 400") + public void testReleaseWithBlankUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .delete("/team//release") + .then() + .statusCode(400); + } + + @Test + @DisplayName("DELETE /team/{uuid}/release with valid uuid returns 204") + public void testReleaseWithValidUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .delete("/team/" + VALID_UUID + "/release") + .then() + .statusCode(204); + } + + @Test + @DisplayName("DELETE /team/{uuid}/release multiple times") + public void testReleaseWithMultipleUuids() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .delete("/team/" + VALID_UUID + "/release") + .then() + .statusCode(204); + + given() + .contentType(ContentType.JSON) + .when() + .delete("/team/" + ANOTHER_UUID + "/release") + .then() + .statusCode(204); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename without game returns 400") + public void testRenameWithoutGameStarted() { + String requestBody = "{\"newNickname\": \"NewName\"}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .patch("/team/" + VALID_UUID + "/rename") + .then() + .statusCode(400); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename with blank uuid returns 400") + public void testRenameWithBlankUuid() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"newNickname\": \"NewName\"}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .patch("/team//rename") + .then() + .statusCode(400); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename without body returns 400") + public void testRenameWithMissingRequestBody() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .patch("/team/" + VALID_UUID + "/rename") + .then() + .statusCode(400); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename with valid request returns 200") + public void testRenameWithValidRequest() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"newNickname\": \"NewName\"}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .patch("/team/" + VALID_UUID + "/rename") + .then() + .statusCode(200); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename returns JSON") + public void testRenameContentType() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"newNickname\": \"NewName\"}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .patch("/team/" + VALID_UUID + "/rename") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("PATCH /team/{uuid}/rename with special characters") + public void testRenameWithSpecialCharacters() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + String requestBody = "{\"newNickname\": \"New-Name_123\"}"; + given() + .contentType(ContentType.JSON) + .body(requestBody) + .when() + .patch("/team/" + VALID_UUID + "/rename") + .then() + .statusCode(200); + } +} diff --git a/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/YakadexResourceTest.java b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/YakadexResourceTest.java new file mode 100644 index 0000000..ebcadd5 --- /dev/null +++ b/yakamon/src/test/java/fr/epita/assistants/yakamon_testsuite/YakadexResourceTest.java @@ -0,0 +1,328 @@ +package fr.epita.assistants.yakamon_testsuite; + +import static io.restassured.RestAssured.given; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@DisplayName("YakadexResourceTest") +public class YakadexResourceTest { + + @Test + @DisplayName("GET /yakadex without game returns 400") + public void testGetYakadexWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /yakadex returns 200 when game started") + public void testGetYakadexWithGameStarted() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex with only_missing=true") + public void testGetYakadexWithOnlyMissingTrue() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .queryParam("only_missing", true) + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex with only_missing=false") + public void testGetYakadexWithOnlyMissingFalse() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .queryParam("only_missing", false) + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex with only_missing=TRUE uppercase") + public void testGetYakadexWithOnlyMissingUppercase() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .queryParam("only_missing", "TRUE") + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex returns JSON") + public void testGetYakadexContentType() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /yakadex/{id} without game returns 400") + public void testGetYakadexByIdWithoutGameStarted() { + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/1") + .then() + .statusCode(400); + } + + @Test + @DisplayName("GET /yakadex/{id} with valid id") + public void testGetYakadexByIdWithGameStarted() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/1") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex/{id} with id=0") + public void testGetYakadexByIdWithZero() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/0") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex/{id} with negative id returns 404") + public void testGetYakadexByIdWithNegativeId() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/-1") + .then() + .statusCode(404); + } + + @Test + @DisplayName("GET /yakadex/{id} with large id") + public void testGetYakadexByIdWithLargeId() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/999999") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex/{id} returns JSON") + public void testGetYakadexByIdContentType() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/1") + .then() + .contentType(ContentType.JSON); + } + + @Test + @DisplayName("GET /yakadex/{id} multiple different ids") + public void testGetYakadexByIdMultipleIds() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + int[] ids = { 1, 2, 5, 10, 42, 100 }; + for (int id : ids) { + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/" + id) + .then() + .statusCode(200); + } + } + + @Test + @DisplayName("GET /yakadex/{id} with id=-2 returns 404") + public void testGetYakadexByIdWithAnotherNegativeId() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/-2") + .then() + .statusCode(404); + } + + @Test + @DisplayName("GET /yakadex/{id} with id=-100 returns 404") + public void testGetYakadexByIdWithLargeNegativeId() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex/-100") + .then() + .statusCode(404); + } + + @Test + @DisplayName("GET /yakadex without query params") + public void testGetYakadexWithoutQueryParams() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + + @Test + @DisplayName("GET /yakadex multiple calls") + public void testGetYakadexMultipleCalls() { + String startRequest = + "{\"mapPath\": \"map1.txt\", \"playerName\": \"TestPlayer\"}"; + given() + .contentType(ContentType.JSON) + .body(startRequest) + .when() + .post("/start/"); + + for (int i = 0; i < 3; i++) { + given() + .contentType(ContentType.JSON) + .when() + .get("/yakadex") + .then() + .statusCode(200); + } + } +}