From 2d5943679b31248a5ff6d09d9dd13c32b1645ec1 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:27:16 +0200 Subject: [PATCH 001/172] refactor: split websocket package into lobby/shared and rename GameController to LobbySocketController --- .../codenames/codenames/backend/lobby/Lobby.java | 4 ++-- .../api/LobbySocketController.java} | 11 +++++++---- .../{websocket => lobby/api/dto}/JoinMessage.java | 2 +- .../{websocket => lobby/domain}/Player.java | 2 +- .../backend/lobby/services/LobbyService.java | 2 +- .../config}/WebSocketConfig.java | 2 +- .../{ => shared}/websocket/SessionRegistry.java | 2 +- .../websocket/WebSocketEventListener.java | 2 +- .../backend/chat/ChatControllerTest.java | 2 +- .../api/LobbySocketControllerTest.java} | 15 ++++++++++----- .../api/dto}/JoinMessageTest.java | 2 +- .../{websocket => lobby/domain}/PlayerTest.java | 2 +- .../backend/lobby/services/LobbyServiceTest.java | 2 +- .../config}/WebSocketConfigTest.java | 2 +- .../websocket/SessionRegistryTest.java | 2 +- .../websocket/WebSocketEventListenerTest.java | 3 ++- 16 files changed, 33 insertions(+), 24 deletions(-) rename src/main/java/com/codenames/codenames/backend/{websocket/GameController.java => lobby/api/LobbySocketController.java} (90%) rename src/main/java/com/codenames/codenames/backend/{websocket => lobby/api/dto}/JoinMessage.java (82%) rename src/main/java/com/codenames/codenames/backend/{websocket => lobby/domain}/Player.java (79%) rename src/main/java/com/codenames/codenames/backend/{websocket => shared/config}/WebSocketConfig.java (96%) rename src/main/java/com/codenames/codenames/backend/{ => shared}/websocket/SessionRegistry.java (96%) rename src/main/java/com/codenames/codenames/backend/{ => shared}/websocket/WebSocketEventListener.java (95%) rename src/test/java/com/codenames/codenames/backend/{websocket/GameControllerTest.java => lobby/api/LobbySocketControllerTest.java} (91%) rename src/test/java/com/codenames/codenames/backend/{websocket => lobby/api/dto}/JoinMessageTest.java (87%) rename src/test/java/com/codenames/codenames/backend/{websocket => lobby/domain}/PlayerTest.java (84%) rename src/test/java/com/codenames/codenames/backend/{websocket => shared/config}/WebSocketConfigTest.java (97%) rename src/test/java/com/codenames/codenames/backend/{ => shared}/websocket/SessionRegistryTest.java (92%) rename src/test/java/com/codenames/codenames/backend/{ => shared}/websocket/WebSocketEventListenerTest.java (98%) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java index 05303511..71c9eb03 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java @@ -2,7 +2,7 @@ import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.websocket.Player; +import com.codenames.codenames.backend.lobby.domain.Player; import java.security.SecureRandom; import java.util.HashMap; import java.util.List; @@ -146,4 +146,4 @@ public Role getPlayerRole(String username) { public Team decideStartingTeam() { return random.nextBoolean() ? Team.RED : Team.BLUE; } -} \ No newline at end of file +} diff --git a/src/main/java/com/codenames/codenames/backend/websocket/GameController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java similarity index 90% rename from src/main/java/com/codenames/codenames/backend/websocket/GameController.java rename to src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 3cec2aee..166cda63 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/GameController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -1,7 +1,10 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.api; +import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.playingfield.GameService; +import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.handler.annotation.MessageMapping; @@ -17,7 +20,7 @@ */ @Slf4j @Controller -public class GameController { +public class LobbySocketController { private final LobbyService lobbyService; private final GameService gameService; @@ -25,14 +28,14 @@ public class GameController { private final SessionRegistry sessionRegistry; /** - * Creates a new {@code GameController}. + * Creates a new {@code LobbySocketController}. * * @param lobbyService the service handling lobby operations * @param gameService the service handling game state retrieval * @param messagingTemplate the messaging template used for broadcasting updates * @param sessionRegistry the registry managing WebSocket sessions */ - public GameController( + public LobbySocketController( LobbyService lobbyService, GameService gameService, SimpMessagingTemplate messagingTemplate, diff --git a/src/main/java/com/codenames/codenames/backend/websocket/JoinMessage.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java similarity index 82% rename from src/main/java/com/codenames/codenames/backend/websocket/JoinMessage.java rename to src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java index 24567a3e..185f503b 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/JoinMessage.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.api.dto; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/codenames/codenames/backend/websocket/Player.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java similarity index 79% rename from src/main/java/com/codenames/codenames/backend/websocket/Player.java rename to src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java index 38b6cd83..a2697933 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/Player.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.domain; /** * Represents a player connected to the system. diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java index 91b542db..0fdcda68 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java @@ -6,7 +6,7 @@ import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.websocket.Player; +import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/codenames/codenames/backend/websocket/WebSocketConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java similarity index 96% rename from src/main/java/com/codenames/codenames/backend/websocket/WebSocketConfig.java rename to src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java index 908f0286..985471e4 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/WebSocketConfig.java +++ b/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/codenames/codenames/backend/websocket/SessionRegistry.java b/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java similarity index 96% rename from src/main/java/com/codenames/codenames/backend/websocket/SessionRegistry.java rename to src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java index 2aee26c6..fb36b2fe 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/SessionRegistry.java +++ b/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.websocket; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/codenames/codenames/backend/websocket/WebSocketEventListener.java b/src/main/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListener.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/websocket/WebSocketEventListener.java rename to src/main/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListener.java index c2982111..672d5992 100644 --- a/src/main/java/com/codenames/codenames/backend/websocket/WebSocketEventListener.java +++ b/src/main/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListener.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.websocket; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java index 6a18a085..51950def 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java @@ -10,7 +10,7 @@ import com.codenames.codenames.backend.utility.ChatMessageType; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.websocket.SessionRegistry; +import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; diff --git a/src/test/java/com/codenames/codenames/backend/websocket/GameControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java similarity index 91% rename from src/test/java/com/codenames/codenames/backend/websocket/GameControllerTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index 815bde5d..d95f2059 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/GameControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.api; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -9,26 +9,30 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import java.util.List; + +import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessagingTemplate; /** - * Unit tests for {@link GameController}. + * Unit tests for {@link LobbySocketController}. * *

Uses mocks to verify interactions with dependencies. */ -class GameControllerTest { +class LobbySocketControllerTest { private LobbyService lobbyService; private GameService gameService; private SessionRegistry sessionRegistry; - private GameController controller; + private LobbySocketController controller; private SimpMessagingTemplate messagingTemplate; @BeforeEach @@ -38,7 +42,8 @@ void setup() { messagingTemplate = mock(SimpMessagingTemplate.class); sessionRegistry = new SessionRegistry(); - controller = new GameController(lobbyService, gameService, messagingTemplate, sessionRegistry); + controller = + new LobbySocketController(lobbyService, gameService, messagingTemplate, sessionRegistry); } @Test diff --git a/src/test/java/com/codenames/codenames/backend/websocket/JoinMessageTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessageTest.java similarity index 87% rename from src/test/java/com/codenames/codenames/backend/websocket/JoinMessageTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessageTest.java index fe644721..68593cd8 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/JoinMessageTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessageTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.api.dto; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/codenames/codenames/backend/websocket/PlayerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java similarity index 84% rename from src/test/java/com/codenames/codenames/backend/websocket/PlayerTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java index 33f1b002..2edc0ea1 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/PlayerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.lobby.domain; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java index 3363a43e..b6adbe47 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java @@ -18,7 +18,7 @@ import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.websocket.Player; +import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/websocket/WebSocketConfigTest.java b/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java similarity index 97% rename from src/test/java/com/codenames/codenames/backend/websocket/WebSocketConfigTest.java rename to src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java index 3d095c72..99147ba4 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/WebSocketConfigTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.config; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/src/test/java/com/codenames/codenames/backend/websocket/SessionRegistryTest.java b/src/test/java/com/codenames/codenames/backend/shared/websocket/SessionRegistryTest.java similarity index 92% rename from src/test/java/com/codenames/codenames/backend/websocket/SessionRegistryTest.java rename to src/test/java/com/codenames/codenames/backend/shared/websocket/SessionRegistryTest.java index f2abf949..2e6dc268 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/SessionRegistryTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/websocket/SessionRegistryTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.websocket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/src/test/java/com/codenames/codenames/backend/websocket/WebSocketEventListenerTest.java b/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java similarity index 98% rename from src/test/java/com/codenames/codenames/backend/websocket/WebSocketEventListenerTest.java rename to src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java index 88577291..fe7543fa 100644 --- a/src/test/java/com/codenames/codenames/backend/websocket/WebSocketEventListenerTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.websocket; +package com.codenames.codenames.backend.shared.websocket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import java.util.Map; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.web.socket.messaging.SessionDisconnectEvent; From 812d6b957b604da285eaf8a219d8753091a6a396 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:30:51 +0200 Subject: [PATCH 002/172] style: fix imports --- src/main/java/com/codenames/codenames/backend/lobby/Lobby.java | 2 +- .../codenames/backend/lobby/services/LobbyService.java | 2 +- .../codenames/codenames/backend/chat/ChatControllerTest.java | 2 +- .../codenames/backend/lobby/api/LobbySocketControllerTest.java | 3 +-- .../codenames/backend/lobby/services/LobbyServiceTest.java | 2 +- .../backend/shared/websocket/WebSocketEventListenerTest.java | 1 - 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java index 71c9eb03..f6f034d2 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.lobby; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.lobby.domain.Player; import java.security.SecureRandom; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java index 0fdcda68..c33816d4 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java @@ -2,11 +2,11 @@ import com.codenames.codenames.backend.chat.ChatService; import com.codenames.codenames.backend.lobby.Lobby; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java index 51950def..6cbb186a 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java @@ -7,10 +7,10 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import com.codenames.codenames.backend.utility.ChatMessageType; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index d95f2059..9b73cf89 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -14,9 +14,8 @@ import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; -import java.util.List; - import com.codenames.codenames.backend.shared.websocket.SessionRegistry; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java index b6adbe47..812583e7 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java @@ -14,11 +14,11 @@ import com.codenames.codenames.backend.chat.ChatService; import com.codenames.codenames.backend.lobby.Lobby; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; -import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java b/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java index fe7543fa..6dc1e250 100644 --- a/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/websocket/WebSocketEventListenerTest.java @@ -6,7 +6,6 @@ import java.lang.reflect.Field; import java.util.Map; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.web.socket.messaging.SessionDisconnectEvent; From 2c9bcea8177736612ba219db61bbd242c50ee82c Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:38:16 +0200 Subject: [PATCH 003/172] refactor: migrate playingfield package into game domain and application --- .../codenames/backend/clue/ClueValidationService.java | 4 ++-- .../{playingfield => game/application}/CardGenerator.java | 3 ++- .../{playingfield => game/application}/GameService.java | 4 +++- .../backend/game/controller/GameSocketController.java | 2 +- .../backend/{playingfield => game/domain}/Board.java | 3 ++- .../backend/{playingfield => game/domain}/Card.java | 2 +- .../{playingfield => game/domain}/GameManager.java | 3 ++- .../{playingfield => game/domain}/GameManagerFactory.java | 3 ++- .../backend/lobby/api/LobbySocketController.java | 2 +- .../codenames/backend/lobby/services/LobbyService.java | 2 +- .../backend/recovery/SystemStatePersistenceService.java | 2 +- .../backend/recovery/SystemStateRecoveryService.java | 6 +++--- .../backend/serialization/DataTransferObjectService.java | 4 ++-- .../codenames/backend/clue/ClueValidationServiceTest.java | 6 +++--- .../application}/CardGeneratorTest.java | 3 ++- .../application}/GameServiceTest.java | 4 +++- .../backend/game/controller/GameSocketControllerTest.java | 2 +- .../backend/{playingfield => game/domain}/BoardTest.java | 3 ++- .../backend/{playingfield => game/domain}/CardTest.java | 2 +- .../domain}/GameManagerFactoryTest.java | 3 ++- .../{playingfield => game/domain}/GameManagerTest.java | 3 ++- .../backend/lobby/api/LobbySocketControllerTest.java | 2 +- .../backend/lobby/services/LobbyServiceTest.java | 2 +- .../recovery/SystemStatePersistenceServiceTest.java | 2 +- .../backend/recovery/SystemStateRecoveryServiceTest.java | 8 ++++---- .../serialization/DataTransferObjectServiceTest.java | 4 ++-- .../backend/serialization/SerializationJsonTest.java | 2 +- 27 files changed, 49 insertions(+), 37 deletions(-) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/application}/CardGenerator.java (96%) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/application}/GameService.java (96%) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/domain}/Board.java (95%) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/domain}/Card.java (94%) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/domain}/GameManager.java (98%) rename src/main/java/com/codenames/codenames/backend/{playingfield => game/domain}/GameManagerFactory.java (92%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/application}/CardGeneratorTest.java (94%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/application}/GameServiceTest.java (96%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/domain}/BoardTest.java (95%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/domain}/CardTest.java (95%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/domain}/GameManagerFactoryTest.java (97%) rename src/test/java/com/codenames/codenames/backend/{playingfield => game/domain}/GameManagerTest.java (99%) diff --git a/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java b/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java index 3ad973bf..21cee71d 100644 --- a/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java +++ b/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.clue; -import com.codenames.codenames.backend.playingfield.Board; -import com.codenames.codenames.backend.playingfield.Card; +import com.codenames.codenames.backend.game.domain.Board; +import com.codenames.codenames.backend.game.domain.Card; import java.util.List; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/CardGenerator.java b/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java similarity index 96% rename from src/main/java/com/codenames/codenames/backend/playingfield/CardGenerator.java rename to src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java index af2d1d2b..6970d9c6 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/CardGenerator.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java @@ -1,5 +1,6 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.application; +import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.utility.Color; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java similarity index 96% rename from src/main/java/com/codenames/codenames/backend/playingfield/GameService.java rename to src/main/java/com/codenames/codenames/backend/game/application/GameService.java index a3ec0b52..9f0a9b2c 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -1,6 +1,8 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.application; import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.serialization.DataTransferObjectService; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; diff --git a/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java index 59d41e9b..04a274d1 100644 --- a/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java @@ -1,11 +1,11 @@ package com.codenames.codenames.backend.game.controller; import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.dto.ClueMessage; import com.codenames.codenames.backend.game.dto.PassTurnMessage; import com.codenames.codenames.backend.game.dto.RevealCardMessage; import com.codenames.codenames.backend.game.dto.StartGameMessage; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/Board.java b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/playingfield/Board.java rename to src/main/java/com/codenames/codenames/backend/game/domain/Board.java index 4e9cefc6..65b2d3ef 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/Board.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java @@ -1,5 +1,6 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.utility.Color; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/Card.java b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/playingfield/Card.java rename to src/main/java/com/codenames/codenames/backend/game/domain/Card.java index 70d6c353..c30d25ed 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/Card.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import com.codenames.codenames.backend.utility.Color; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java similarity index 98% rename from src/main/java/com/codenames/codenames/backend/playingfield/GameManager.java rename to src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 81aca161..61384831 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -1,7 +1,8 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import com.codenames.codenames.backend.clue.Clue; import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.serialization.CardDataTransferObject; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; diff --git a/src/main/java/com/codenames/codenames/backend/playingfield/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java similarity index 92% rename from src/main/java/com/codenames/codenames/backend/playingfield/GameManagerFactory.java rename to src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 63b468ab..10ba3245 100644 --- a/src/main/java/com/codenames/codenames/backend/playingfield/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -1,6 +1,7 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 166cda63..5110afba 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -2,8 +2,8 @@ import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java index c33816d4..7f03bdbe 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java @@ -1,10 +1,10 @@ package com.codenames.codenames.backend.lobby.services; import com.codenames.codenames.backend.chat.ChatService; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java b/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java index 17bf2406..08c8b2d6 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java index 1425196d..059facc2 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java @@ -1,11 +1,11 @@ package com.codenames.codenames.backend.recovery; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.GameManager; -import com.codenames.codenames.backend.playingfield.GameManagerFactory; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import java.util.Comparator; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java index f54d3442..a002443b 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.serialization; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.playingfield.Card; -import com.codenames.codenames.backend.playingfield.GameManager; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java b/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java index 972f677f..c3ec6edc 100644 --- a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java @@ -7,9 +7,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.playingfield.Board; -import com.codenames.codenames.backend.playingfield.Card; -import com.codenames.codenames.backend.playingfield.CardGenerator; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.domain.Board; +import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.utility.Color; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/CardGeneratorTest.java b/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java similarity index 94% rename from src/test/java/com/codenames/codenames/backend/playingfield/CardGeneratorTest.java rename to src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java index eed02baa..8c6ea678 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/CardGeneratorTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java @@ -1,8 +1,9 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.utility.Color; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java similarity index 96% rename from src/test/java/com/codenames/codenames/backend/playingfield/GameServiceTest.java rename to src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index c8b01b29..6909bf1f 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -10,6 +10,8 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.serialization.CardDataTransferObject; import com.codenames.codenames.backend.serialization.DataTransferObjectService; diff --git a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java index 44022629..0ecffd61 100644 --- a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java @@ -6,11 +6,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.dto.ClueMessage; import com.codenames.codenames.backend.game.dto.PassTurnMessage; import com.codenames.codenames.backend.game.dto.RevealCardMessage; import com.codenames.codenames.backend.game.dto.StartGameMessage; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/BoardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java similarity index 95% rename from src/test/java/com/codenames/codenames/backend/playingfield/BoardTest.java rename to src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java index 1f6308c1..e0877393 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/BoardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -10,6 +10,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.utility.Color; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/CardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java similarity index 95% rename from src/test/java/com/codenames/codenames/backend/playingfield/CardTest.java rename to src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java index 8c9a0b0d..295b5b89 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/CardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java similarity index 97% rename from src/test/java/com/codenames/codenames/backend/playingfield/GameManagerFactoryTest.java rename to src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 25e72ad7..b4bdcfb0 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -7,6 +7,7 @@ import static org.mockito.Mockito.mock; import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.serialization.CardDataTransferObject; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; diff --git a/src/test/java/com/codenames/codenames/backend/playingfield/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java similarity index 99% rename from src/test/java/com/codenames/codenames/backend/playingfield/GameManagerTest.java rename to src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 615386a3..18763cbe 100644 --- a/src/test/java/com/codenames/codenames/backend/playingfield/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.playingfield; +package com.codenames.codenames.backend.game.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -14,6 +14,7 @@ import com.codenames.codenames.backend.clue.Clue; import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.serialization.CardDataTransferObject; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index 9b73cf89..3fbafaa4 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -9,10 +9,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java index 812583e7..e48d282e 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java @@ -13,10 +13,10 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.chat.ChatService; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java index c0d956dc..3a1d38f5 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java @@ -6,9 +6,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java index a0d7528d..86a258e0 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java @@ -7,15 +7,15 @@ import com.codenames.codenames.backend.chat.ChatService; import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyCodeGenerator; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.playingfield.CardGenerator; -import com.codenames.codenames.backend.playingfield.GameManager; -import com.codenames.codenames.backend.playingfield.GameManagerFactory; -import com.codenames.codenames.backend.playingfield.GameService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.serialization.CardDataTransferObject; import com.codenames.codenames.backend.serialization.DataTransferObjectService; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index 12050802..fc8b7d8e 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -6,8 +6,8 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.clue.Clue; -import com.codenames.codenames.backend.playingfield.Card; -import com.codenames.codenames.backend.playingfield.GameManager; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index e2b109c1..b543b094 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.playingfield.Card; +import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; From 15ec64d29eb36033970ae41aac8c3364b07c6c50 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:43:56 +0200 Subject: [PATCH 004/172] refactor: separate game dto models from mapping services --- .../codenames/backend/game/application/GameService.java | 4 ++-- .../codenames/backend/game/domain/GameManager.java | 4 ++-- .../codenames/backend/game/domain/GameManagerFactory.java | 2 +- .../{serialization => game/dto}/CardDataTransferObject.java | 2 +- .../dto}/GameStateDataTransferObject.java | 2 +- .../mapping}/DataTransferObjectService.java | 4 +++- .../backend/recovery/SystemStateRecoveryService.java | 2 +- .../codenames/backend/recovery/snapshot/SystemSnapshot.java | 2 +- .../codenames/backend/serialization/SerializationJson.java | 1 + .../codenames/backend/game/application/GameServiceTest.java | 6 +++--- .../backend/game/controller/GameSocketControllerTest.java | 2 +- .../backend/game/domain/GameManagerFactoryTest.java | 4 ++-- .../codenames/backend/game/domain/GameManagerTest.java | 4 ++-- .../backend/lobby/api/LobbySocketControllerTest.java | 2 +- .../codenames/backend/recovery/JsonStateStoreTest.java | 2 +- .../backend/recovery/SystemStatePersistenceServiceTest.java | 2 +- .../backend/recovery/SystemStateRecoveryServiceTest.java | 6 +++--- .../serialization/DataTransferObjectServiceTest.java | 2 ++ .../backend/serialization/SerializationJsonTest.java | 2 ++ 19 files changed, 31 insertions(+), 24 deletions(-) rename src/main/java/com/codenames/codenames/backend/{serialization => game/dto}/CardDataTransferObject.java (86%) rename src/main/java/com/codenames/codenames/backend/{serialization => game/dto}/GameStateDataTransferObject.java (93%) rename src/main/java/com/codenames/codenames/backend/{serialization => game/mapping}/DataTransferObjectService.java (91%) diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index 9f0a9b2c..fd5838c6 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -3,8 +3,8 @@ import com.codenames.codenames.backend.clue.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.serialization.DataTransferObjectService; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 61384831..29e05d91 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -3,8 +3,8 @@ import com.codenames.codenames.backend.clue.Clue; import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.serialization.CardDataTransferObject; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 10ba3245..99c2021e 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -2,7 +2,7 @@ import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/CardDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java similarity index 86% rename from src/main/java/com/codenames/codenames/backend/serialization/CardDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java index e367571e..c69a4fc9 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/CardDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.serialization; +package com.codenames.codenames.backend.game.dto; import com.codenames.codenames.backend.utility.Color; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/GameStateDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java similarity index 93% rename from src/main/java/com/codenames/codenames/backend/serialization/GameStateDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java index 95f9c982..e97ba743 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/GameStateDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.serialization; +package com.codenames.codenames.backend.game.dto; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.utility.Role; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java similarity index 91% rename from src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java rename to src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index a002443b..ec8ec8b0 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -1,6 +1,8 @@ -package com.codenames.codenames.backend.serialization; +package com.codenames.codenames.backend.game.mapping; import com.codenames.codenames.backend.game.dto.ClueDto; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.utility.Color; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java index 059facc2..4e17fe33 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java @@ -7,7 +7,7 @@ import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.Comparator; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java index 3ae69bb4..0d25d952 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery.snapshot; import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java index ba08f680..abc89dbc 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java +++ b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java @@ -1,5 +1,6 @@ package com.codenames.codenames.backend.serialization; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index 6909bf1f..e66686a9 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -13,9 +13,9 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.serialization.CardDataTransferObject; -import com.codenames.codenames.backend.serialization.DataTransferObjectService; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java index 0ecffd61..36cfec05 100644 --- a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java @@ -12,7 +12,7 @@ import com.codenames.codenames.backend.game.dto.RevealCardMessage; import com.codenames.codenames.backend.game.dto.StartGameMessage; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index b4bdcfb0..11808722 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -9,8 +9,8 @@ import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.serialization.CardDataTransferObject; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 18763cbe..85ec9548 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -16,8 +16,8 @@ import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.serialization.CardDataTransferObject; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index 3fbafaa4..b9960f86 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -13,7 +13,7 @@ import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java index b03acf68..a7ea9681 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java @@ -9,7 +9,7 @@ import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java index 3a1d38f5..56e1d842 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java @@ -10,7 +10,7 @@ import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java index 86a258e0..88992465 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java @@ -17,9 +17,9 @@ import com.codenames.codenames.backend.lobby.services.LobbyCodeGenerator; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.serialization.CardDataTransferObject; -import com.codenames.codenames.backend.serialization.DataTransferObjectService; -import com.codenames.codenames.backend.serialization.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index fc8b7d8e..f4133825 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -6,8 +6,10 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; import com.codenames.codenames.backend.utility.Team; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index b543b094..e12969de 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -6,7 +6,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.dto.ClueDto; +import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.utility.Color; import com.codenames.codenames.backend.utility.Role; From c692f35c0391af47290cf02cd6dedc5fa7674f5d Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:46:46 +0200 Subject: [PATCH 005/172] refactor: move utility enums into feature-owned packages --- .../codenames/codenames/backend/chat/ChatController.java | 6 +++--- .../java/com/codenames/codenames/backend/chat/ChatDto.java | 2 +- .../backend/{utility => chat/api/dto}/ChatMessageType.java | 2 +- .../codenames/backend/game/application/CardGenerator.java | 2 +- .../codenames/backend/game/application/GameService.java | 2 +- .../com/codenames/codenames/backend/game/domain/Board.java | 2 +- .../com/codenames/codenames/backend/game/domain/Card.java | 2 +- .../codenames/backend/{utility => game/domain}/Color.java | 2 +- .../codenames/backend/game/domain/GameManager.java | 6 +++--- .../codenames/backend/game/domain/GameManagerFactory.java | 2 +- .../codenames/backend/game/dto/CardDataTransferObject.java | 2 +- .../codenames/codenames/backend/game/dto/ClueMessage.java | 2 +- .../backend/game/dto/GameStateDataTransferObject.java | 4 ++-- .../codenames/backend/game/dto/PassTurnMessage.java | 2 +- .../codenames/backend/game/dto/RevealCardMessage.java | 2 +- .../backend/game/mapping/DataTransferObjectService.java | 6 +++--- .../java/com/codenames/codenames/backend/lobby/Lobby.java | 4 ++-- .../codenames/backend/{utility => lobby/domain}/Role.java | 2 +- .../codenames/backend/{utility => lobby/domain}/Team.java | 2 +- .../codenames/codenames/backend/lobby/dto/PlayerDto.java | 4 ++-- .../codenames/backend/lobby/services/LobbyService.java | 4 ++-- .../codenames/backend/chat/ChatControllerTest.java | 6 +++--- .../codenames/codenames/backend/chat/ChatHistoryTest.java | 2 +- .../codenames/codenames/backend/chat/ChatServiceTest.java | 4 ++-- .../codenames/backend/clue/ClueValidationServiceTest.java | 2 +- .../backend/game/application/CardGeneratorTest.java | 2 +- .../codenames/backend/game/application/GameServiceTest.java | 6 +++--- .../backend/game/controller/GameSocketControllerTest.java | 2 +- .../codenames/codenames/backend/game/domain/BoardTest.java | 2 +- .../codenames/codenames/backend/game/domain/CardTest.java | 2 +- .../backend/game/domain/GameManagerFactoryTest.java | 6 +++--- .../codenames/backend/game/domain/GameManagerTest.java | 6 +++--- .../com/codenames/codenames/backend/lobby/LobbyTest.java | 4 ++-- .../backend/lobby/controller/LobbyControllerTest.java | 4 ++-- .../codenames/backend/lobby/services/LobbyServiceTest.java | 4 ++-- .../codenames/backend/recovery/JsonStateStoreTest.java | 4 ++-- .../backend/recovery/SystemStateRecoveryServiceTest.java | 6 +++--- .../serialization/DataTransferObjectServiceTest.java | 6 +++--- .../backend/serialization/SerializationJsonTest.java | 6 +++--- 39 files changed, 68 insertions(+), 68 deletions(-) rename src/main/java/com/codenames/codenames/backend/{utility => chat/api/dto}/ChatMessageType.java (63%) rename src/main/java/com/codenames/codenames/backend/{utility => game/domain}/Color.java (69%) rename src/main/java/com/codenames/codenames/backend/{utility => lobby/domain}/Role.java (66%) rename src/main/java/com/codenames/codenames/backend/{utility => lobby/domain}/Team.java (54%) diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java b/src/main/java/com/codenames/codenames/backend/chat/ChatController.java index 91c22ddc..f1d4847c 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java +++ b/src/main/java/com/codenames/codenames/backend/chat/ChatController.java @@ -1,9 +1,9 @@ package com.codenames.codenames.backend.chat; import com.codenames.codenames.backend.lobby.services.LobbyService; -import com.codenames.codenames.backend.utility.ChatMessageType; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java b/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java index 8cb695da..837cd6f6 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java +++ b/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.chat; -import com.codenames.codenames.backend.utility.ChatMessageType; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; /** * Record class for messages that is used in controller. diff --git a/src/main/java/com/codenames/codenames/backend/utility/ChatMessageType.java b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java similarity index 63% rename from src/main/java/com/codenames/codenames/backend/utility/ChatMessageType.java rename to src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java index daeb3731..c6f78b84 100644 --- a/src/main/java/com/codenames/codenames/backend/utility/ChatMessageType.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.utility; +package com.codenames.codenames.backend.chat.api.dto; /** * Enum representing the type of chat message. diff --git a/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java b/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java index 6970d9c6..5c36eedc 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/CardGenerator.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.game.application; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index fd5838c6..0a994fcb 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -5,7 +5,7 @@ import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/Board.java b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java index 65b2d3ef..a92940b9 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/Board.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.game.domain; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import java.util.ArrayList; import java.util.List; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java index c30d25ed..0d8ca3b3 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import lombok.Getter; /** Represents a single card on a board. */ diff --git a/src/main/java/com/codenames/codenames/backend/utility/Color.java b/src/main/java/com/codenames/codenames/backend/game/domain/Color.java similarity index 69% rename from src/main/java/com/codenames/codenames/backend/utility/Color.java rename to src/main/java/com/codenames/codenames/backend/game/domain/Color.java index 37c4b6f6..3be9e0cf 100644 --- a/src/main/java/com/codenames/codenames/backend/utility/Color.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Color.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.utility; +package com.codenames.codenames.backend.game.domain; /** * Enum representing the color of a card on the board. diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 29e05d91..0bda0594 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -5,9 +5,9 @@ import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 99c2021e..a3b83797 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -3,7 +3,7 @@ import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.stereotype.Component; /** Generates GameManager instances to be used by GameService. */ diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java index c69a4fc9..be00b3da 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.dto; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; /** * Represents the state of a single card, for JSON serialization. diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java b/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java index 6b120c95..e0c1aaee 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.dto; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java index e97ba743..ac1e1c05 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.game.dto; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; /** diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java b/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java index 10812d83..f6392dd4 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.dto; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java b/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java index 940d4676..5298a60c 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.dto; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index ec8ec8b0..d982e0da 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -5,9 +5,9 @@ import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java index f6f034d2..e1e51b9e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.lobby; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.security.SecureRandom; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/utility/Role.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Role.java similarity index 66% rename from src/main/java/com/codenames/codenames/backend/utility/Role.java rename to src/main/java/com/codenames/codenames/backend/lobby/domain/Role.java index 11064f94..8da3a840 100644 --- a/src/main/java/com/codenames/codenames/backend/utility/Role.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Role.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.utility; +package com.codenames.codenames.backend.lobby.domain; /** * Represents the role of a player in the game. diff --git a/src/main/java/com/codenames/codenames/backend/utility/Team.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Team.java similarity index 54% rename from src/main/java/com/codenames/codenames/backend/utility/Team.java rename to src/main/java/com/codenames/codenames/backend/lobby/domain/Team.java index a615f1bb..3fc6a75b 100644 --- a/src/main/java/com/codenames/codenames/backend/utility/Team.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Team.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.utility; +package com.codenames.codenames.backend.lobby.domain; /** Represents the teams. */ public enum Team { diff --git a/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java b/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java index 94edb594..be9d94d0 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.lobby.dto; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; /** * A data transfer object for communicating with the frontend, holds user-specific information. diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java index 7f03bdbe..73bf1aca 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java @@ -5,8 +5,8 @@ import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java index 6cbb186a..65969016 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java @@ -8,9 +8,9 @@ import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; -import com.codenames.codenames.backend.utility.ChatMessageType; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java index 3552dff7..9213a864 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java @@ -3,7 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.codenames.codenames.backend.utility.ChatMessageType; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java index 0607b1e3..b33496a3 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java @@ -8,8 +8,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import com.codenames.codenames.backend.utility.ChatMessageType; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java b/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java index c3ec6edc..a9ffd345 100644 --- a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java @@ -10,7 +10,7 @@ import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.domain.Board; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java b/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java index 8c6ea678..ca651072 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/CardGeneratorTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index e66686a9..382a4cbd 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -16,9 +16,9 @@ import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java index 36cfec05..a3f8fbf1 100644 --- a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java @@ -13,7 +13,7 @@ import com.codenames.codenames.backend.game.dto.StartGameMessage; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java index e0877393..bd87fed8 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java @@ -11,7 +11,7 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java index 295b5b89..9a0be028 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.utility.Color; +import com.codenames.codenames.backend.game.domain.Color; import org.junit.jupiter.api.Test; /** Unit tests for Card. */ diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 11808722..25a19a25 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -11,9 +11,9 @@ import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 85ec9548..a725d1f0 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -18,9 +18,9 @@ import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java index 7c28cc89..ae124df1 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java @@ -5,8 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java index 3ae261c6..2a800b2a 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java @@ -11,8 +11,8 @@ import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.lobby.services.LobbyService; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java index e48d282e..b171a0ec 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java @@ -17,8 +17,8 @@ import com.codenames.codenames.backend.lobby.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java index a7ea9681..b8b80360 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java @@ -10,8 +10,8 @@ import com.codenames.codenames.backend.lobby.dto.PlayerDto; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.nio.file.Files; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java index 88992465..0cd19b41 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java @@ -20,9 +20,9 @@ import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Path; import java.util.HashMap; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index f4133825..372a9c1e 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -10,9 +10,9 @@ import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index e12969de..23e39a03 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -10,9 +10,9 @@ import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.utility.Color; -import com.codenames.codenames.backend.utility.Role; -import com.codenames.codenames.backend.utility.Team; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; From 2063659713b60b251f5e49df1e8c2715da7aed1a Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:50:03 +0200 Subject: [PATCH 006/172] refactor: align lobby package to api application domain structure --- .../codenames/codenames/backend/chat/ChatController.java | 2 +- .../lobby/{controller => api}/LobbyController.java | 8 ++++---- .../backend/lobby/api/LobbySocketController.java | 2 +- .../backend/lobby/{ => api}/dto/LobbyResponse.java | 2 +- .../codenames/backend/lobby/{ => api}/dto/PlayerDto.java | 2 +- .../{services => application}/LobbyCodeGenerator.java | 2 +- .../lobby/{services => application}/LobbyService.java | 6 +++--- .../codenames/backend/lobby/{ => domain}/Lobby.java | 2 +- .../backend/recovery/SystemStatePersistenceService.java | 2 +- .../backend/recovery/SystemStateRecoveryService.java | 6 +++--- .../backend/recovery/snapshot/SystemSnapshot.java | 2 +- .../codenames/backend/chat/ChatControllerTest.java | 2 +- .../lobby/{controller => api}/LobbyControllerTest.java | 6 +++--- .../backend/lobby/api/LobbySocketControllerTest.java | 2 +- .../{services => application}/LobbyCodeGeneratorTest.java | 2 +- .../lobby/{services => application}/LobbyServiceTest.java | 6 +++--- .../codenames/backend/lobby/{ => domain}/LobbyTest.java | 2 +- .../codenames/backend/recovery/JsonStateStoreTest.java | 2 +- .../recovery/SystemStatePersistenceServiceTest.java | 4 ++-- .../backend/recovery/SystemStateRecoveryServiceTest.java | 8 ++++---- 20 files changed, 35 insertions(+), 35 deletions(-) rename src/main/java/com/codenames/codenames/backend/lobby/{controller => api}/LobbyController.java (96%) rename src/main/java/com/codenames/codenames/backend/lobby/{ => api}/dto/LobbyResponse.java (85%) rename src/main/java/com/codenames/codenames/backend/lobby/{ => api}/dto/PlayerDto.java (90%) rename src/main/java/com/codenames/codenames/backend/lobby/{services => application}/LobbyCodeGenerator.java (93%) rename src/main/java/com/codenames/codenames/backend/lobby/{services => application}/LobbyService.java (98%) rename src/main/java/com/codenames/codenames/backend/lobby/{ => domain}/Lobby.java (98%) rename src/test/java/com/codenames/codenames/backend/lobby/{controller => api}/LobbyControllerTest.java (97%) rename src/test/java/com/codenames/codenames/backend/lobby/{services => application}/LobbyCodeGeneratorTest.java (90%) rename src/test/java/com/codenames/codenames/backend/lobby/{services => application}/LobbyServiceTest.java (98%) rename src/test/java/com/codenames/codenames/backend/lobby/{ => domain}/LobbyTest.java (98%) diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java b/src/main/java/com/codenames/codenames/backend/chat/ChatController.java index f1d4847c..addd0f05 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java +++ b/src/main/java/com/codenames/codenames/backend/chat/ChatController.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.chat; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/controller/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java similarity index 96% rename from src/main/java/com/codenames/codenames/backend/lobby/controller/LobbyController.java rename to src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 4415ca4e..0b6712fb 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/controller/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -1,8 +1,8 @@ -package com.codenames.codenames.backend.lobby.controller; +package com.codenames.codenames.backend.lobby.api; -import com.codenames.codenames.backend.lobby.dto.LobbyResponse; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.api.dto.LobbyResponse; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; import java.util.List; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 5110afba..ea3f7911 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -3,7 +3,7 @@ import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/dto/LobbyResponse.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java similarity index 85% rename from src/main/java/com/codenames/codenames/backend/lobby/dto/LobbyResponse.java rename to src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java index 1b750c27..bf864a20 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/dto/LobbyResponse.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.dto; +package com.codenames.codenames.backend.lobby.api.dto; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java similarity index 90% rename from src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java rename to src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java index be9d94d0..395c7e71 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/dto/PlayerDto.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.dto; +package com.codenames.codenames.backend.lobby.api.dto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGenerator.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGenerator.java similarity index 93% rename from src/main/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGenerator.java rename to src/main/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGenerator.java index c878bcf2..7f24e08e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGenerator.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGenerator.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.services; +package com.codenames.codenames.backend.lobby.application; import java.security.SecureRandom; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java similarity index 98% rename from src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java rename to src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index 73bf1aca..ea0ba9d1 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/services/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -1,10 +1,10 @@ -package com.codenames.codenames.backend.lobby.services; +package com.codenames.codenames.backend.lobby.application; import com.codenames.codenames.backend.chat.ChatService; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.Lobby; +import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java similarity index 98% rename from src/main/java/com/codenames/codenames/backend/lobby/Lobby.java rename to src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index e1e51b9e..1e3b5891 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby; +package com.codenames.codenames.backend.lobby.domain; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.domain.Role; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java b/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java index 08c8b2d6..531b5e27 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java index 4e17fe33..fd1a12fc 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java @@ -3,9 +3,9 @@ import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.lobby.Lobby; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.Comparator; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java index 0d25d952..64bae86f 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.recovery.snapshot; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java index 65969016..a30b0d8e 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java @@ -6,7 +6,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.lobby.domain.Role; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java similarity index 97% rename from src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index 2a800b2a..fa4e00b3 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/controller/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.controller; +package com.codenames.codenames.backend.lobby.api; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -8,8 +8,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index b9960f86..f11a5a7f 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -12,7 +12,7 @@ import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGeneratorTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGeneratorTest.java similarity index 90% rename from src/test/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGeneratorTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGeneratorTest.java index 9b1395d7..62467d33 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyCodeGeneratorTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyCodeGeneratorTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.services; +package com.codenames.codenames.backend.lobby.application; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java similarity index 98% rename from src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index b171a0ec..fb311f56 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/services/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby.services; +package com.codenames.codenames.backend.lobby.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -14,9 +14,9 @@ import com.codenames.codenames.backend.chat.ChatService; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.Lobby; +import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java similarity index 98% rename from src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java rename to src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index ae124df1..dfab70d6 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.lobby; +package com.codenames.codenames.backend.lobby.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java index b8b80360..8c122ebe 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Role; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java index 56e1d842..a193b336 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java @@ -7,8 +7,8 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java index 0cd19b41..97e02013 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java @@ -12,10 +12,10 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.lobby.Lobby; -import com.codenames.codenames.backend.lobby.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.services.LobbyCodeGenerator; -import com.codenames.codenames.backend.lobby.services.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; From ac43e9f871d6b9c0434725cca7ceab4d74ecfcea Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:51:33 +0200 Subject: [PATCH 007/172] refactor: consolidate game websocket api structure --- .../backend/game/{controller => api}/GameSocketController.java | 2 +- .../game/{controller => api}/GameSocketControllerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/com/codenames/codenames/backend/game/{controller => api}/GameSocketController.java (98%) rename src/test/java/com/codenames/codenames/backend/game/{controller => api}/GameSocketControllerTest.java (98%) diff --git a/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java similarity index 98% rename from src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java rename to src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 04a274d1..6a9f86fa 100644 --- a/src/main/java/com/codenames/codenames/backend/game/controller/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.controller; +package com.codenames.codenames.backend.game.api; import com.codenames.codenames.backend.clue.Clue; import com.codenames.codenames.backend.game.application.GameService; diff --git a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java similarity index 98% rename from src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java rename to src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index a3f8fbf1..f3edf964 100644 --- a/src/test/java/com/codenames/codenames/backend/game/controller/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.controller; +package com.codenames.codenames.backend.game.api; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; From 1f719168da2530ce83b28e2d8fa2302dc52f1c3e Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:56:34 +0200 Subject: [PATCH 008/172] refactor: organize chat package into api application and domain --- .../codenames/backend/chat/{ => api}/ChatController.java | 4 +++- .../codenames/backend/chat/{ => api/dto}/ChatDto.java | 2 +- .../codenames/backend/chat/{ => application}/ChatService.java | 4 +++- .../codenames/backend/chat/{ => domain}/ChatHistory.java | 3 ++- .../codenames/backend/lobby/application/LobbyService.java | 2 +- .../codenames/backend/chat/{ => api}/ChatControllerTest.java | 4 +++- .../backend/chat/{ => application}/ChatServiceTest.java | 3 ++- .../codenames/backend/chat/{ => domain}/ChatHistoryTest.java | 3 ++- .../codenames/backend/lobby/application/LobbyServiceTest.java | 2 +- .../backend/recovery/SystemStateRecoveryServiceTest.java | 2 +- 10 files changed, 19 insertions(+), 10 deletions(-) rename src/main/java/com/codenames/codenames/backend/chat/{ => api}/ChatController.java (95%) rename src/main/java/com/codenames/codenames/backend/chat/{ => api/dto}/ChatDto.java (86%) rename src/main/java/com/codenames/codenames/backend/chat/{ => application}/ChatService.java (94%) rename src/main/java/com/codenames/codenames/backend/chat/{ => domain}/ChatHistory.java (89%) rename src/test/java/com/codenames/codenames/backend/chat/{ => api}/ChatControllerTest.java (96%) rename src/test/java/com/codenames/codenames/backend/chat/{ => application}/ChatServiceTest.java (96%) rename src/test/java/com/codenames/codenames/backend/chat/{ => domain}/ChatHistoryTest.java (93%) diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java b/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/chat/ChatController.java rename to src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java index addd0f05..64382cd9 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatController.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java @@ -1,6 +1,8 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.api; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java similarity index 86% rename from src/main/java/com/codenames/codenames/backend/chat/ChatDto.java rename to src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java index 837cd6f6..b6d4ba47 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatDto.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.api.dto; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatService.java b/src/main/java/com/codenames/codenames/backend/chat/application/ChatService.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/chat/ChatService.java rename to src/main/java/com/codenames/codenames/backend/chat/application/ChatService.java index 15efe4ac..bb7f0c6e 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatService.java +++ b/src/main/java/com/codenames/codenames/backend/chat/application/ChatService.java @@ -1,5 +1,7 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.application; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; +import com.codenames.codenames.backend.chat.domain.ChatHistory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/codenames/codenames/backend/chat/ChatHistory.java b/src/main/java/com/codenames/codenames/backend/chat/domain/ChatHistory.java similarity index 89% rename from src/main/java/com/codenames/codenames/backend/chat/ChatHistory.java rename to src/main/java/com/codenames/codenames/backend/chat/domain/ChatHistory.java index 3cc10cab..26e46015 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/ChatHistory.java +++ b/src/main/java/com/codenames/codenames/backend/chat/domain/ChatHistory.java @@ -1,5 +1,6 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.domain; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index ea0ba9d1..21dff21b 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.lobby.application; -import com.codenames.codenames.backend.chat.ChatService; +import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java similarity index 96% rename from src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java rename to src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java index a30b0d8e..356c876b 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.api; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; @@ -6,7 +6,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.lobby.domain.Role; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java b/src/test/java/com/codenames/codenames/backend/chat/application/ChatServiceTest.java similarity index 96% rename from src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java rename to src/test/java/com/codenames/codenames/backend/chat/application/ChatServiceTest.java index b33496a3..38edcc88 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/application/ChatServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -8,6 +8,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.stream.Stream; diff --git a/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java b/src/test/java/com/codenames/codenames/backend/chat/domain/ChatHistoryTest.java similarity index 93% rename from src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java rename to src/test/java/com/codenames/codenames/backend/chat/domain/ChatHistoryTest.java index 9213a864..e273a1a2 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/ChatHistoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/domain/ChatHistoryTest.java @@ -1,8 +1,9 @@ -package com.codenames.codenames.backend.chat; +package com.codenames.codenames.backend.chat.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index fb311f56..66b3a426 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -12,7 +12,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.chat.ChatService; +import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java index 97e02013..7b094edc 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.chat.ChatService; +import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.clue.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.GameService; From 578dd01fef855f252f03c0b0c4bd9f6c51e5c155 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 01:59:35 +0200 Subject: [PATCH 009/172] refactor: split recovery package into application domain and infrastructure --- .../codenames/backend/game/api/GameSocketController.java | 2 +- .../codenames/backend/lobby/api/LobbyController.java | 2 +- .../{ => application}/SystemStatePersistenceService.java | 5 +++-- .../{ => application}/SystemStateRecoveryService.java | 5 +++-- .../recovery/{ => domain}/snapshot/SystemSnapshot.java | 2 +- .../recovery/{ => infrastructure}/JsonStateStore.java | 4 ++-- .../codenames/backend/game/api/GameSocketControllerTest.java | 2 +- .../codenames/backend/lobby/api/LobbyControllerTest.java | 2 +- .../{ => application}/SystemStatePersistenceServiceTest.java | 5 +++-- .../{ => application}/SystemStateRecoveryServiceTest.java | 5 +++-- .../recovery/{ => infrastructure}/JsonStateStoreTest.java | 4 ++-- 11 files changed, 21 insertions(+), 17 deletions(-) rename src/main/java/com/codenames/codenames/backend/recovery/{ => application}/SystemStatePersistenceService.java (87%) rename src/main/java/com/codenames/codenames/backend/recovery/{ => application}/SystemStateRecoveryService.java (95%) rename src/main/java/com/codenames/codenames/backend/recovery/{ => domain}/snapshot/SystemSnapshot.java (90%) rename src/main/java/com/codenames/codenames/backend/recovery/{ => infrastructure}/JsonStateStore.java (95%) rename src/test/java/com/codenames/codenames/backend/recovery/{ => application}/SystemStatePersistenceServiceTest.java (89%) rename src/test/java/com/codenames/codenames/backend/recovery/{ => application}/SystemStateRecoveryServiceTest.java (98%) rename src/test/java/com/codenames/codenames/backend/recovery/{ => infrastructure}/JsonStateStoreTest.java (97%) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 6a9f86fa..5d1d014e 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -6,7 +6,7 @@ import com.codenames.codenames.backend.game.dto.PassTurnMessage; import com.codenames.codenames.backend.game.dto.RevealCardMessage; import com.codenames.codenames.backend.game.dto.StartGameMessage; -import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 0b6712fb..03aa213a 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -3,7 +3,7 @@ import com.codenames.codenames.backend.lobby.api.dto.LobbyResponse; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java similarity index 87% rename from src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java rename to src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java index 531b5e27..3b27f6dc 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java @@ -1,8 +1,9 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.application; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; import org.springframework.stereotype.Service; /** diff --git a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java rename to src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index fd1a12fc..077bfb3a 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.application; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.GameManager; @@ -6,7 +6,8 @@ import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.Comparator; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java similarity index 90% rename from src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java rename to src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java index 64bae86f..ada4d349 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.recovery.snapshot; +package com.codenames.codenames.backend.recovery.domain.snapshot; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/JsonStateStore.java b/src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/recovery/JsonStateStore.java rename to src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java index 1c58c2ca..2975d543 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/JsonStateStore.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java @@ -1,6 +1,6 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.infrastructure; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.nio.file.AtomicMoveNotSupportedException; diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index f3edf964..35d288a5 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -11,7 +11,7 @@ import com.codenames.codenames.backend.game.dto.PassTurnMessage; import com.codenames.codenames.backend.game.dto.RevealCardMessage; import com.codenames.codenames.backend.game.dto.StartGameMessage; -import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index fa4e00b3..565cdb67 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -10,7 +10,7 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.SystemStatePersistenceService; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java similarity index 89% rename from src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java rename to src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java index a193b336..bb2cade8 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -9,7 +9,8 @@ import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java similarity index 98% rename from src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java rename to src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 7b094edc..66fa8045 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.application; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -16,7 +16,8 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java similarity index 97% rename from src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java rename to src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 8c122ebe..0674d372 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.recovery; +package com.codenames.codenames.backend.recovery.infrastructure; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -8,7 +8,7 @@ import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.recovery.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; From 7364514c2d273357916ef5a6f74a8514a1823f40 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 02:04:14 +0200 Subject: [PATCH 010/172] refactor: move jackson config to shared and align clue classes under game --- .../codenames/backend/game/api/GameSocketController.java | 2 +- .../{clue => game/application}/ClueValidationService.java | 2 +- .../codenames/backend/game/application/GameService.java | 2 +- .../codenames/backend/{clue => game/domain}/Clue.java | 2 +- .../codenames/codenames/backend/game/domain/GameManager.java | 4 ++-- .../codenames/backend/game/domain/GameManagerFactory.java | 2 +- .../codenames/backend/{ => shared}/config/JacksonConfig.java | 2 +- .../{clue => game/application}/ClueValidationServiceTest.java | 2 +- .../codenames/backend/game/application/GameServiceTest.java | 2 +- .../codenames/backend/{clue => game/domain}/ClueTest.java | 2 +- .../codenames/backend/game/domain/GameManagerFactoryTest.java | 2 +- .../codenames/backend/game/domain/GameManagerTest.java | 4 ++-- .../recovery/application/SystemStateRecoveryServiceTest.java | 2 +- .../backend/serialization/DataTransferObjectServiceTest.java | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) rename src/main/java/com/codenames/codenames/backend/{clue => game/application}/ClueValidationService.java (94%) rename src/main/java/com/codenames/codenames/backend/{clue => game/domain}/Clue.java (93%) rename src/main/java/com/codenames/codenames/backend/{ => shared}/config/JacksonConfig.java (91%) rename src/test/java/com/codenames/codenames/backend/{clue => game/application}/ClueValidationServiceTest.java (97%) rename src/test/java/com/codenames/codenames/backend/{clue => game/domain}/ClueTest.java (94%) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 5d1d014e..7c39280b 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.api; -import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.dto.ClueMessage; import com.codenames.codenames.backend.game.dto.PassTurnMessage; diff --git a/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java b/src/main/java/com/codenames/codenames/backend/game/application/ClueValidationService.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java rename to src/main/java/com/codenames/codenames/backend/game/application/ClueValidationService.java index 21cee71d..78e58ad6 100644 --- a/src/main/java/com/codenames/codenames/backend/clue/ClueValidationService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/ClueValidationService.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.clue; +package com.codenames.codenames.backend.game.application; import com.codenames.codenames.backend.game.domain.Board; import com.codenames.codenames.backend.game.domain.Card; diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index 0a994fcb..c3352c68 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.application; -import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; diff --git a/src/main/java/com/codenames/codenames/backend/clue/Clue.java b/src/main/java/com/codenames/codenames/backend/game/domain/Clue.java similarity index 93% rename from src/main/java/com/codenames/codenames/backend/clue/Clue.java rename to src/main/java/com/codenames/codenames/backend/game/domain/Clue.java index bf067c1b..08fe9abe 100644 --- a/src/main/java/com/codenames/codenames/backend/clue/Clue.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Clue.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.clue; +package com.codenames.codenames.backend.game.domain; /** Clue class that holds the clue word and guess amount + 1. */ public record Clue(String word, int guessAmount) { diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 0bda0594..b89d2cae 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.clue.Clue; -import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.domain.Clue; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index a3b83797..278a444e 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/main/java/com/codenames/codenames/backend/config/JacksonConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/JacksonConfig.java similarity index 91% rename from src/main/java/com/codenames/codenames/backend/config/JacksonConfig.java rename to src/main/java/com/codenames/codenames/backend/shared/config/JacksonConfig.java index 61227b4f..7ffb1bd5 100644 --- a/src/main/java/com/codenames/codenames/backend/config/JacksonConfig.java +++ b/src/main/java/com/codenames/codenames/backend/shared/config/JacksonConfig.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.config; +package com.codenames.codenames.backend.shared.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; diff --git a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java similarity index 97% rename from src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java rename to src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java index a9ffd345..cd3cbdd5 100644 --- a/src/test/java/com/codenames/codenames/backend/clue/ClueValidationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.clue; +package com.codenames.codenames.backend.game.application; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index 382a4cbd..fce52767 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -9,7 +9,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.dto.ClueDto; diff --git a/src/test/java/com/codenames/codenames/backend/clue/ClueTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/ClueTest.java similarity index 94% rename from src/test/java/com/codenames/codenames/backend/clue/ClueTest.java rename to src/test/java/com/codenames/codenames/backend/game/domain/ClueTest.java index d761b362..04f51c19 100644 --- a/src/test/java/com/codenames/codenames/backend/clue/ClueTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/ClueTest.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.clue; +package com.codenames.codenames.backend.game.domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 25a19a25..9d79338c 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; -import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index a725d1f0..9f2b09cd 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -12,8 +12,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.clue.Clue; -import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.domain.Clue; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.dto.ClueDto; import com.codenames.codenames.backend.game.dto.CardDataTransferObject; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 66fa8045..4f6305cd 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.codenames.codenames.backend.chat.application.ChatService; -import com.codenames.codenames.backend.clue.ClueValidationService; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.GameManager; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index 372a9c1e..1a13161a 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -5,7 +5,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.clue.Clue; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; From c55271aad7b4fb41694f667d6ea09157de04ba32 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 18:08:34 +0200 Subject: [PATCH 011/172] refactor: move game dto package under game api --- .../backend/game/api/GameSocketController.java | 8 ++++---- .../game/{ => api}/dto/CardDataTransferObject.java | 2 +- .../codenames/backend/game/{ => api}/dto/ClueDto.java | 2 +- .../backend/game/{ => api}/dto/ClueMessage.java | 2 +- .../{ => api}/dto/GameStateDataTransferObject.java | 4 ++-- .../backend/game/{ => api}/dto/PassTurnMessage.java | 2 +- .../backend/game/{ => api}/dto/RevealCardMessage.java | 2 +- .../backend/game/{ => api}/dto/StartGameMessage.java | 2 +- .../backend/game/application/GameService.java | 2 +- .../codenames/backend/game/domain/GameManager.java | 4 ++-- .../backend/game/domain/GameManagerFactory.java | 2 +- .../game/mapping/DataTransferObjectService.java | 6 +++--- .../application/SystemStateRecoveryService.java | 2 +- .../recovery/domain/snapshot/SystemSnapshot.java | 2 +- .../backend/serialization/SerializationJson.java | 2 +- .../backend/game/api/GameSocketControllerTest.java | 10 +++++----- .../backend/game/application/GameServiceTest.java | 6 +++--- .../backend/game/domain/GameManagerFactoryTest.java | 6 +++--- .../codenames/backend/game/domain/GameManagerTest.java | 6 +++--- .../backend/lobby/api/LobbySocketControllerTest.java | 2 +- .../application/SystemStatePersistenceServiceTest.java | 2 +- .../application/SystemStateRecoveryServiceTest.java | 6 +++--- .../recovery/infrastructure/JsonStateStoreTest.java | 4 ++-- .../serialization/DataTransferObjectServiceTest.java | 2 +- .../backend/serialization/SerializationJsonTest.java | 6 +++--- 25 files changed, 47 insertions(+), 47 deletions(-) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/CardDataTransferObject.java (87%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/ClueDto.java (87%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/ClueMessage.java (87%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/GameStateDataTransferObject.java (85%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/PassTurnMessage.java (86%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/RevealCardMessage.java (87%) rename src/main/java/com/codenames/codenames/backend/game/{ => api}/dto/StartGameMessage.java (82%) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 7c39280b..af780d7d 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -2,10 +2,10 @@ import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.game.dto.ClueMessage; -import com.codenames.codenames.backend.game.dto.PassTurnMessage; -import com.codenames.codenames.backend.game.dto.RevealCardMessage; -import com.codenames.codenames.backend.game.dto.StartGameMessage; +import com.codenames.codenames.backend.game.api.dto.ClueMessage; +import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; +import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; +import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java similarity index 87% rename from src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java index be00b3da..c3c8d05a 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/CardDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; import com.codenames.codenames.backend.game.domain.Color; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/ClueDto.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/ClueDto.java similarity index 87% rename from src/main/java/com/codenames/codenames/backend/game/dto/ClueDto.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/ClueDto.java index 26a54187..31c3fcc8 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/ClueDto.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/ClueDto.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; /** * Data transfer object for a clue, containing the clue word and the allowed number of guesses. diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/ClueMessage.java similarity index 87% rename from src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/ClueMessage.java index e0c1aaee..289d3de5 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/ClueMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/ClueMessage.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java similarity index 85% rename from src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java index ac1e1c05..5bef69db 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/GameStateDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java @@ -1,6 +1,6 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; -import com.codenames.codenames.backend.game.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/PassTurnMessage.java similarity index 86% rename from src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/PassTurnMessage.java index f6392dd4..a8cc721c 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/PassTurnMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/PassTurnMessage.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/RevealCardMessage.java similarity index 87% rename from src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/RevealCardMessage.java index 5298a60c..71c889ad 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/RevealCardMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/RevealCardMessage.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; import com.codenames.codenames.backend.lobby.domain.Team; import lombok.Getter; diff --git a/src/main/java/com/codenames/codenames/backend/game/dto/StartGameMessage.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/StartGameMessage.java similarity index 82% rename from src/main/java/com/codenames/codenames/backend/game/dto/StartGameMessage.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/StartGameMessage.java index e8d846aa..0a26de03 100644 --- a/src/main/java/com/codenames/codenames/backend/game/dto/StartGameMessage.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/StartGameMessage.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.game.dto; +package com.codenames.codenames.backend.game.api.dto; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index c3352c68..75050f7b 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -4,7 +4,7 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index b89d2cae..78c2e01b 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -3,8 +3,8 @@ import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 278a444e..4ca15cbf 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -2,7 +2,7 @@ import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index d982e0da..147f5522 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.game.mapping; -import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.Color; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index 077bfb3a..72c22adf 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -8,7 +8,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import java.util.Comparator; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java index ada4d349..d97fa417 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery.domain.snapshot; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java index abc89dbc..e1cdbea6 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java +++ b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.serialization; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 35d288a5..c4f8a46c 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -7,12 +7,12 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.game.dto.ClueMessage; -import com.codenames.codenames.backend.game.dto.PassTurnMessage; -import com.codenames.codenames.backend.game.dto.RevealCardMessage; -import com.codenames.codenames.backend.game.dto.StartGameMessage; +import com.codenames.codenames.backend.game.api.dto.ClueMessage; +import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; +import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; +import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index fce52767..d88c6faa 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -12,10 +12,10 @@ import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 9d79338c..daf00e48 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -8,9 +8,9 @@ import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 9f2b09cd..3b7eda82 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -15,9 +15,9 @@ import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index f11a5a7f..fa519077 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -13,7 +13,7 @@ import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java index bb2cade8..80af0112 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java @@ -11,7 +11,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 4f6305cd..78dea956 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -11,16 +11,16 @@ import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 0674d372..9974a9ff 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -6,10 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.game.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index 1a13161a..8d6bae84 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -6,7 +6,7 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 23e39a03..365d7bc6 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -6,9 +6,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.dto.ClueDto; -import com.codenames.codenames.backend.game.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; From ae578bd71050197194b166cb896fc70fa67bf2f6 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 18:12:15 +0200 Subject: [PATCH 012/172] refactor: standardize game dto class naming --- ...rdDataTransferObject.java => CardDto.java} | 2 +- ...aTransferObject.java => GameStateDto.java} | 4 +- .../backend/game/application/GameService.java | 8 ++-- .../backend/game/domain/GameManager.java | 8 ++-- .../game/domain/GameManagerFactory.java | 4 +- .../mapping/DataTransferObjectService.java | 18 ++++----- .../SystemStateRecoveryService.java | 6 +-- .../domain/snapshot/SystemSnapshot.java | 4 +- .../serialization/SerializationJson.java | 4 +- .../game/api/GameSocketControllerTest.java | 14 +++---- .../game/application/GameServiceTest.java | 24 ++++++------ .../game/domain/GameManagerFactoryTest.java | 30 +++++++-------- .../backend/game/domain/GameManagerTest.java | 38 +++++++++---------- .../lobby/api/LobbySocketControllerTest.java | 6 +-- .../SystemStatePersistenceServiceTest.java | 4 +- .../SystemStateRecoveryServiceTest.java | 18 ++++----- .../infrastructure/JsonStateStoreTest.java | 8 ++-- .../DataTransferObjectServiceTest.java | 16 ++++---- .../serialization/SerializationJsonTest.java | 12 +++--- 19 files changed, 114 insertions(+), 114 deletions(-) rename src/main/java/com/codenames/codenames/backend/game/api/dto/{CardDataTransferObject.java => CardDto.java} (79%) rename src/main/java/com/codenames/codenames/backend/game/api/dto/{GameStateDataTransferObject.java => GameStateDto.java} (89%) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDto.java similarity index 79% rename from src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/CardDto.java index c3c8d05a..060c92a8 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/CardDto.java @@ -9,4 +9,4 @@ * @param color the color of the card (could also be "hidden") * @param isGuessed the guess state of the card */ -public record CardDataTransferObject(String word, Color color, boolean isGuessed) {} +public record CardDto(String word, Color color, boolean isGuessed) {} diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java similarity index 89% rename from src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java rename to src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java index 5bef69db..156c6801 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDataTransferObject.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java @@ -14,9 +14,9 @@ * @param currentClue the current clue object, consisting of word and amount of guesses * @param cardList the cards on the board */ -public record GameStateDataTransferObject( +public record GameStateDto( Team winner, Team currentTurn, Role currentPhase, ClueDto currentClue, - List cardList) {} + List cardList) {} diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index 75050f7b..54f7d7b9 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -4,7 +4,7 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -126,9 +126,9 @@ public void passTurn(String lobbyCode, Team callingTeam) { * @param lobbyCode the unique lobby code * @return the mapped game state transfer object */ - public GameStateDataTransferObject getCurrentGameState(String lobbyCode) { + public GameStateDto getCurrentGameState(String lobbyCode) { GameManager gm = getGame(lobbyCode); - return dtoService.createGameStateDataTransferObject( + return dtoService.createGameStateDto( gm, gm.getCurrentTurn(), gm.getCurrentPhase()); } @@ -153,7 +153,7 @@ public boolean isGameStarted(String lobbyCode) { * * @return map of lobby codes to game state dto snapshots */ - public Map getGameSnapshots() { + public Map getGameSnapshots() { return games.keySet().stream() .collect( java.util.stream.Collectors.toMap(lobbyCode -> lobbyCode, this::getCurrentGameState)); diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 78c2e01b..b2991414 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -3,8 +3,8 @@ import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -74,7 +74,7 @@ public GameManager( * @param clueValidationService clue validation service */ public GameManager( - GameStateDataTransferObject state, ClueValidationService clueValidationService) { + GameStateDto state, ClueValidationService clueValidationService) { if (state.cardList() == null || state.cardList().isEmpty()) { throw new IllegalArgumentException("cards cannot be null or empty"); } @@ -101,7 +101,7 @@ public GameManager( this.board = new Board(cards); } - private Card toCard(CardDataTransferObject cardDto) { + private Card toCard(CardDto cardDto) { Card card = new Card(cardDto.word(), cardDto.color()); if (cardDto.isGuessed()) { card.setIsGuessedTrue(); diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 4ca15cbf..86d1dce1 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -2,7 +2,7 @@ import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.stereotype.Component; @@ -40,7 +40,7 @@ public GameManager create(Team startingTeam) { * @param snapshot persisted game state snapshot * @return restored game manager */ - public GameManager createFromSnapshot(GameStateDataTransferObject snapshot) { + public GameManager createFromSnapshot(GameStateDto snapshot) { return new GameManager(snapshot, clueValidationService); } } diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index 147f5522..7d06cd24 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.game.mapping; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.Color; @@ -22,9 +22,9 @@ public class DataTransferObjectService { * @param card card object from the board * @return the card DTO for the game state DTO */ - private CardDataTransferObject createCardDataTransferObject(Card card) { + private CardDto createCardDto(Card card) { Color displayColor = card.getColor(); - return new CardDataTransferObject(card.getWord(), displayColor, card.isGuessed()); + return new CardDto(card.getWord(), displayColor, card.isGuessed()); } /** @@ -34,16 +34,16 @@ private CardDataTransferObject createCardDataTransferObject(Card card) { * @param currentTurn the current turn * @return a DTO of the current game state */ - public GameStateDataTransferObject createGameStateDataTransferObject( + public GameStateDto createGameStateDto( GameManager gameManager, Team currentTurn, Role currentPhase) { List cardList = gameManager.getCardList(); - List cardDataTransferObject = new ArrayList<>(); + List cardDataTransferObject = new ArrayList<>(); for (Card card : cardList) { - cardDataTransferObject.add(createCardDataTransferObject(card)); + cardDataTransferObject.add(createCardDto(card)); } if (gameManager.getCurrentClue() == null) { - return new GameStateDataTransferObject( + return new GameStateDto( gameManager.getWinner(), currentTurn, currentPhase, @@ -52,7 +52,7 @@ public GameStateDataTransferObject createGameStateDataTransferObject( } String word = gameManager.getCurrentClue().word(); int guessAmount = gameManager.getCurrentClue().guessAmount(); - return new GameStateDataTransferObject( + return new GameStateDto( gameManager.getWinner(), currentTurn, currentPhase, diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index 72c22adf..1b518e48 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -8,7 +8,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -85,11 +85,11 @@ private void restoreLobbies(Map> lobbySnapshots) { * * @param gameSnapshots persisted game states keyed by lobby code */ - private void restoreGames(Map gameSnapshots) { + private void restoreGames(Map gameSnapshots) { if (gameSnapshots == null || gameSnapshots.isEmpty()) { return; } - for (Map.Entry entry : gameSnapshots.entrySet()) { + for (Map.Entry entry : gameSnapshots.entrySet()) { GameManager restoredGame = gameManagerFactory.createFromSnapshot(entry.getValue()); gameService.restoreGameManager(entry.getKey(), restoredGame); } diff --git a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java index d97fa417..4ab1211a 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery.domain.snapshot; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import java.util.List; import java.util.Map; @@ -15,7 +15,7 @@ public record SystemSnapshot( int schemaVersion, Map> lobbies, - Map games) { + Map games) { public static final int CURRENT_SCHEMA_VERSION = 2; } diff --git a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java index e1cdbea6..01c8fc73 100644 --- a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java +++ b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.serialization; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; @@ -26,7 +26,7 @@ public SerializationJson(ObjectMapper mapper) { * @return the string JSON containing: winner, points, list of cards and their information * @throws IllegalStateException if serialization fails */ - public String serialize(GameStateDataTransferObject gameStateDataTransferObject) { + public String serialize(GameStateDto gameStateDataTransferObject) { try { return mapper.writeValueAsString(gameStateDataTransferObject); } catch (JsonProcessingException e) { diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index c4f8a46c..379d9f90 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -12,7 +12,7 @@ import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -47,7 +47,7 @@ void startGameShouldBroadcastStateWithoutPersisting() { message.setLobbyCode(LOBBY_CODE); when(gameService.getCurrentGameState(LOBBY_CODE)) - .thenReturn(createGameStateDataTransferObject()); + .thenReturn(createGameStateDto()); controller.startGame(message); @@ -63,7 +63,7 @@ void revealCardShouldPersistAndBroadcastState() { message.setCurrentTurn(Team.RED); when(gameService.getCurrentGameState(LOBBY_CODE)) - .thenReturn(createGameStateDataTransferObject()); + .thenReturn(createGameStateDto()); controller.revealCard(message); @@ -81,7 +81,7 @@ void submitClueShouldPersistAndBroadcastState() { message.setCurrentTurn(Team.RED); when(gameService.getCurrentGameState(LOBBY_CODE)) - .thenReturn(createGameStateDataTransferObject()); + .thenReturn(createGameStateDto()); controller.submitClue(message); @@ -97,7 +97,7 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { message.setCurrentTurn(Team.RED); when(gameService.getCurrentGameState(LOBBY_CODE)) - .thenReturn(createGameStateDataTransferObject()); + .thenReturn(createGameStateDto()); controller.passTurn(message); @@ -106,7 +106,7 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } - private GameStateDataTransferObject createGameStateDataTransferObject() { - return new GameStateDataTransferObject(null, Team.RED, null, null, List.of()); + private GameStateDto createGameStateDto() { + return new GameStateDto(null, Team.RED, null, null, List.of()); } } diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index d88c6faa..2e5fea2b 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -13,9 +13,9 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -108,20 +108,20 @@ void testRestoreGameManager() { @Test void testGetCurrentGameState() { - GameStateDataTransferObject expected = - new GameStateDataTransferObject( + GameStateDto expected = + new GameStateDto( null, redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), - List.of(new CardDataTransferObject("Dog", Color.RED, false))); + List.of(new CardDto("Dog", Color.RED, false))); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); when(mockGameManager.getCurrentPhase()).thenReturn(Role.SPYMASTER); when(mockGameManager.getRemainingGuesses()).thenReturn(2); - when(mockDtoService.createGameStateDataTransferObject(mockGameManager, redTeam, Role.SPYMASTER)) + when(mockDtoService.createGameStateDto(mockGameManager, redTeam, Role.SPYMASTER)) .thenReturn(expected); - GameStateDataTransferObject result = gameService.getCurrentGameState(lobbyCode); + GameStateDto result = gameService.getCurrentGameState(lobbyCode); assertEquals(expected, result); } @@ -138,20 +138,20 @@ void testIsGameStartedWhenGameDoesNotExist() { @Test void getGameSnapshotsShouldReturnAllCurrentGameStates() { - GameStateDataTransferObject expected = - new GameStateDataTransferObject( + GameStateDto expected = + new GameStateDto( null, redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), - List.of(new CardDataTransferObject("Dog", Color.RED, false))); + List.of(new CardDto("Dog", Color.RED, false))); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); when(mockGameManager.getCurrentPhase()).thenReturn(Role.SPYMASTER); - when(mockDtoService.createGameStateDataTransferObject(mockGameManager, redTeam, Role.SPYMASTER)) + when(mockDtoService.createGameStateDto(mockGameManager, redTeam, Role.SPYMASTER)) .thenReturn(expected); - Map snapshots = gameService.getGameSnapshots(); + Map snapshots = gameService.getGameSnapshots(); assertEquals(1, snapshots.size()); assertEquals(expected, snapshots.get(lobbyCode)); diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index daf00e48..fbfad08e 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -9,8 +9,8 @@ import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -39,15 +39,15 @@ void testCreate() { @Test void testCreateFromSnapshotWithClue() { - GameStateDataTransferObject snapshot = - new GameStateDataTransferObject( + GameStateDto snapshot = + new GameStateDto( Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of( - new CardDataTransferObject("Dog", Color.RED, true), - new CardDataTransferObject("Cat", Color.BLUE, false))); + new CardDto("Dog", Color.RED, true), + new CardDto("Cat", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -63,13 +63,13 @@ void testCreateFromSnapshotWithClue() { @Test void testCreateFromSnapshotWithoutClue() { - GameStateDataTransferObject snapshot = - new GameStateDataTransferObject( + GameStateDto snapshot = + new GameStateDto( null, Team.BLUE, Role.SPYMASTER, null, - List.of(new CardDataTransferObject("Tree", Color.BLUE, false))); + List.of(new CardDto("Tree", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -81,17 +81,17 @@ void testCreateFromSnapshotWithoutClue() { @Test void testCreateFromSnapshotMapsCardsAndCountsGuessedCardsByColor() { - GameStateDataTransferObject snapshot = - new GameStateDataTransferObject( + GameStateDto snapshot = + new GameStateDto( null, Team.RED, Role.OPERATIVE, null, List.of( - new CardDataTransferObject("Dog", Color.RED, true), - new CardDataTransferObject("Cat", Color.RED, false), - new CardDataTransferObject("Tree", Color.BLUE, true), - new CardDataTransferObject("Sun", Color.NEUTRAL, true))); + new CardDto("Dog", Color.RED, true), + new CardDto("Cat", Color.RED, false), + new CardDto("Tree", Color.BLUE, true), + new CardDto("Sun", Color.NEUTRAL, true))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 3b7eda82..e2bf9521 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -16,8 +16,8 @@ import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -336,8 +336,8 @@ void testPassTurn_throwsWhenSpymaster() { @Test void recoveryConstructorThrowsWhenCardsAreNull() { - GameStateDataTransferObject state = - new GameStateDataTransferObject(null, Team.RED, Role.SPYMASTER, null, null); + GameStateDto state = + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, null); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -345,8 +345,8 @@ void recoveryConstructorThrowsWhenCardsAreNull() { @Test void recoveryConstructorThrowsWhenCardsAreEmpty() { - GameStateDataTransferObject state = - new GameStateDataTransferObject(null, Team.RED, Role.SPYMASTER, null, List.of()); + GameStateDto state = + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, List.of()); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -354,11 +354,11 @@ void recoveryConstructorThrowsWhenCardsAreEmpty() { @Test void recoveryConstructorThrowsWhenCurrentTurnIsNull() { - List cards = - List.of(new CardDataTransferObject("Dog", Color.RED, false)); + List cards = + List.of(new CardDto("Dog", Color.RED, false)); - GameStateDataTransferObject state = - new GameStateDataTransferObject(null, null, Role.SPYMASTER, null, cards); + GameStateDto state = + new GameStateDto(null, null, Role.SPYMASTER, null, cards); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -366,11 +366,11 @@ void recoveryConstructorThrowsWhenCurrentTurnIsNull() { @Test void recoveryConstructorThrowsWhenCurrentPhaseIsNull() { - List cards = - List.of(new CardDataTransferObject("Dog", Color.RED, false)); + List cards = + List.of(new CardDto("Dog", Color.RED, false)); - GameStateDataTransferObject state = - new GameStateDataTransferObject(null, Team.RED, null, null, cards); + GameStateDto state = + new GameStateDto(null, Team.RED, null, null, cards); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -378,13 +378,13 @@ void recoveryConstructorThrowsWhenCurrentPhaseIsNull() { @Test void recoveryConstructorRestoresPersistedState() { - List cards = + List cards = List.of( - new CardDataTransferObject("Dog", Color.RED, true), - new CardDataTransferObject("Cat", Color.BLUE, false)); + new CardDto("Dog", Color.RED, true), + new CardDto("Cat", Color.BLUE, false)); - GameStateDataTransferObject state = - new GameStateDataTransferObject( + GameStateDto state = + new GameStateDto( Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), cards); GameManager restored = new GameManager(state, mockClueValidationService); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index fa519077..b77048b8 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -13,7 +13,7 @@ import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -165,7 +165,7 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { verify(messagingTemplate).convertAndSend(eq("/topic/game/ABCDE"), any(Object.class)); } - private GameStateDataTransferObject createGameStatePayload() { - return new GameStateDataTransferObject(null, null, null, null, List.of()); + private GameStateDto createGameStatePayload() { + return new GameStateDto(null, null, null, null, List.of()); } } diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java index 80af0112..b522f7d8 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java @@ -11,7 +11,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -27,7 +27,7 @@ void persistCurrentStateSavesCurrentSystemSnapshot() { Map> lobbySnapshots = Map.of("ABCDE", List.of()); - Map gameSnapshots = Map.of(); + Map gameSnapshots = Map.of(); when(lobbyService.getLobbySnapshots()).thenReturn(lobbySnapshots); when(gameService.getGameSnapshots()).thenReturn(gameSnapshots); diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 78dea956..cdb760cb 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -18,9 +18,9 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -66,15 +66,15 @@ void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { List.of( new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - GameStateDataTransferObject gameSnapshot = - new GameStateDataTransferObject( + GameStateDto gameSnapshot = + new GameStateDto( null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of( - new CardDataTransferObject("Dog", Color.RED, true), - new CardDataTransferObject("Cat", Color.BLUE, false))); + new CardDto("Dog", Color.RED, true), + new CardDto("Cat", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, @@ -208,13 +208,13 @@ void recoverOnStartupRestoresLobbyWhenSomeTeamOrRoleValuesAreMissing() { @Test void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); - GameStateDataTransferObject gameSnapshot = - new GameStateDataTransferObject( + GameStateDto gameSnapshot = + new GameStateDto( null, Team.BLUE, Role.SPYMASTER, null, - List.of(new CardDataTransferObject("Tree", Color.BLUE, false))); + List.of(new CardDto("Tree", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 9974a9ff..9a8e3035 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -9,7 +9,7 @@ import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import com.fasterxml.jackson.databind.ObjectMapper; @@ -57,8 +57,8 @@ void saveAndLoadRoundTrip() { new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - GameStateDataTransferObject gameSnapshot = - new GameStateDataTransferObject( + GameStateDto gameSnapshot = + new GameStateDto( null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of()); SystemSnapshot expectedSnapshot = @@ -85,7 +85,7 @@ void saveAndLoadRoundTrip() { assertEquals(Role.SPYMASTER, actualPlayers.get(0).role()); assertTrue(actualPlayers.get(0).isHost()); - GameStateDataTransferObject actualGameSnapshot = actualSnapshot.games().get("ABCDE"); + GameStateDto actualGameSnapshot = actualSnapshot.games().get("ABCDE"); assertEquals(Team.RED, actualGameSnapshot.currentTurn()); assertEquals(Role.OPERATIVE, actualGameSnapshot.currentPhase()); assertEquals("ANIMAL", actualGameSnapshot.currentClue().word()); diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index 8d6bae84..b060d9b5 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -6,7 +6,7 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; @@ -22,7 +22,7 @@ class DataTransferObjectServiceTest { Card cardGuessed; GameManager mockGameManager; DataTransferObjectService service; - GameStateDataTransferObject gameStateDto; + GameStateDto gameStateDto; private static final Team redTeam = Team.RED; private static final Role spymaster = Role.SPYMASTER; private static final Role operative = Role.OPERATIVE; @@ -42,13 +42,13 @@ void setUp() { when(mockGameManager.getRemainingGuesses()).thenReturn(2); gameStateDto = - service.createGameStateDataTransferObject(mockGameManager, redTeam, spymaster); + service.createGameStateDto(mockGameManager, redTeam, spymaster); } @Test void testSpymasterVisibility() { gameStateDto = - service.createGameStateDataTransferObject(mockGameManager, redTeam, spymaster); + service.createGameStateDto(mockGameManager, redTeam, spymaster); assertEquals(Color.RED, gameStateDto.cardList().get(0).color()); } @@ -71,20 +71,20 @@ void testGetWinner_exists() { void testGetWinner_null() { when(mockGameManager.getWinner()).thenReturn(null); gameStateDto = - service.createGameStateDataTransferObject(mockGameManager, redTeam, operative); + service.createGameStateDto(mockGameManager, redTeam, operative); assertNull(gameStateDto.winner()); } @Test - void testCreateGameStateDataTransferObject() { + void testCreateGameStateDto() { Clue clue = new Clue("word", 1); when(mockGameManager.getCardList()).thenReturn(List.of()); when(mockGameManager.getWinner()).thenReturn(null); when(mockGameManager.getCurrentClue()).thenReturn(clue); when(mockGameManager.getRemainingGuesses()).thenReturn(3); - GameStateDataTransferObject dto = - service.createGameStateDataTransferObject(mockGameManager, redTeam, operative); + GameStateDto dto = + service.createGameStateDto(mockGameManager, redTeam, operative); assertEquals(clue.word(), dto.currentClue().word()); assertEquals(clue.guessAmount(), dto.currentClue().guessAmount()); diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 365d7bc6..7aa82d85 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -6,9 +6,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.api.dto.CardDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDataTransferObject; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; @@ -23,8 +23,8 @@ class SerializationJsonTest { SerializationJson serializer; Card card; - List dummyList; - GameStateDataTransferObject dummyGameState; + List dummyList; + GameStateDto dummyGameState; ObjectMapper mapper = new ObjectMapper(); private static final Team redTeam = Team.RED; private static final Role spymaster = Role.SPYMASTER; @@ -34,9 +34,9 @@ void setUp() { card = new Card("TEST", Color.RED); serializer = new SerializationJson(mapper); - dummyList = List.of(new CardDataTransferObject("TEST", null, false)); + dummyList = List.of(new CardDto("TEST", null, false)); dummyGameState = - new GameStateDataTransferObject( + new GameStateDto( redTeam, redTeam, spymaster, new ClueDto("Test", 1), dummyList); } From 4917efa0564a12cd351dbe2195ae1082deb0b3ad Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 18:24:39 +0200 Subject: [PATCH 013/172] style: fix imports --- .../game/api/GameSocketController.java | 4 +- .../backend/game/api/dto/GameStateDto.java | 1 - .../backend/game/application/GameService.java | 2 +- .../backend/game/domain/GameManager.java | 6 +-- .../game/domain/GameManagerFactory.java | 4 +- .../mapping/DataTransferObjectService.java | 4 +- .../SystemStateRecoveryService.java | 4 +- .../domain/snapshot/SystemSnapshot.java | 2 +- .../game/api/GameSocketControllerTest.java | 6 +-- .../game/application/GameServiceTest.java | 8 ++-- .../game/domain/GameManagerFactoryTest.java | 37 ++++++++----------- .../backend/game/domain/GameManagerTest.java | 8 ++-- .../lobby/api/LobbySocketControllerTest.java | 4 +- .../SystemStatePersistenceServiceTest.java | 2 +- .../SystemStateRecoveryServiceTest.java | 18 ++++----- .../infrastructure/JsonStateStoreTest.java | 4 +- .../DataTransferObjectServiceTest.java | 4 +- .../serialization/SerializationJsonTest.java | 3 +- 18 files changed, 54 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index af780d7d..02eb5c0d 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -1,11 +1,11 @@ package com.codenames.codenames.backend.game.api; -import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.api.dto.ClueMessage; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java index 156c6801..c5167d1c 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java @@ -1,6 +1,5 @@ package com.codenames.codenames.backend.game.api.dto; -import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index 54f7d7b9..961bf26f 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -1,10 +1,10 @@ package com.codenames.codenames.backend.game.application; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index b2991414..2a2b98aa 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -1,11 +1,9 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java index 86d1dce1..eb27f9d3 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManagerFactory.java @@ -1,8 +1,8 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index 7d06cd24..453170aa 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -1,11 +1,11 @@ package com.codenames.codenames.backend.game.mapping; -import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index 1b518e48..dfc7d653 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -1,14 +1,14 @@ package com.codenames.codenames.backend.recovery.application; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; import java.util.Comparator; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java index 4ab1211a..835db7f4 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java @@ -1,7 +1,7 @@ package com.codenames.codenames.backend.recovery.domain.snapshot; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import java.util.List; import java.util.Map; diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 379d9f90..28519385 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -6,14 +6,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.api.dto.ClueMessage; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index 2e5fea2b..b5a49e72 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -9,14 +9,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Clue; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index fbfad08e..29500811 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -6,12 +6,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; @@ -45,9 +44,7 @@ void testCreateFromSnapshotWithClue() { Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), - List.of( - new CardDto("Dog", Color.RED, true), - new CardDto("Cat", Color.BLUE, false))); + List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -65,11 +62,7 @@ void testCreateFromSnapshotWithClue() { void testCreateFromSnapshotWithoutClue() { GameStateDto snapshot = new GameStateDto( - null, - Team.BLUE, - Role.SPYMASTER, - null, - List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -82,16 +75,16 @@ void testCreateFromSnapshotWithoutClue() { @Test void testCreateFromSnapshotMapsCardsAndCountsGuessedCardsByColor() { GameStateDto snapshot = - new GameStateDto( - null, - Team.RED, - Role.OPERATIVE, - null, - List.of( - new CardDto("Dog", Color.RED, true), - new CardDto("Cat", Color.RED, false), - new CardDto("Tree", Color.BLUE, true), - new CardDto("Sun", Color.NEUTRAL, true))); + new GameStateDto( + null, + Team.RED, + Role.OPERATIVE, + null, + List.of( + new CardDto("Dog", Color.RED, true), + new CardDto("Cat", Color.RED, false), + new CardDto("Tree", Color.BLUE, true), + new CardDto("Sun", Color.NEUTRAL, true))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index e2bf9521..0725ef67 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -12,13 +12,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index b77048b8..a9cf4201 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -9,11 +9,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; -import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java index b522f7d8..15efbf45 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java @@ -6,12 +6,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index cdb760cb..e050c37a 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -6,24 +6,24 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.codenames.codenames.backend.chat.application.ChatService; -import com.codenames.codenames.backend.game.application.ClueValidationService; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.codenames.codenames.backend.game.api.dto.CardDto; -import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; +import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Path; import java.util.HashMap; diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 9a8e3035..727bc723 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -7,11 +7,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.nio.file.Files; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java index b060d9b5..13501e73 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/DataTransferObjectServiceTest.java @@ -5,12 +5,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Clue; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 7aa82d85..87d64221 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -36,8 +36,7 @@ void setUp() { dummyList = List.of(new CardDto("TEST", null, false)); dummyGameState = - new GameStateDto( - redTeam, redTeam, spymaster, new ClueDto("Test", 1), dummyList); + new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), dummyList); } @Test From ec92ed2bb199ef5c3c74e840ed4fd29685f989d7 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 18:25:10 +0200 Subject: [PATCH 014/172] style: fix imports --- .../application/SystemStateRecoveryServiceTest.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index e050c37a..1aabc3ca 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -72,9 +72,7 @@ void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), - List.of( - new CardDto("Dog", Color.RED, true), - new CardDto("Cat", Color.BLUE, false))); + List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, @@ -210,11 +208,7 @@ void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); GameStateDto gameSnapshot = new GameStateDto( - null, - Team.BLUE, - Role.SPYMASTER, - null, - List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); From e724865c0cb19331261174c9bbc3929290612286 Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 27 May 2026 18:43:13 +0200 Subject: [PATCH 015/172] style: remove unused imports --- .../backend/chat/api/dto/ChatDto.java | 2 -- .../codenames/backend/game/domain/Card.java | 1 - .../codenames/backend/lobby/domain/Lobby.java | 29 +++++++------------ .../ClueValidationServiceTest.java | 1 - .../backend/game/domain/BoardTest.java | 1 - .../backend/game/domain/CardTest.java | 1 - .../backend/lobby/domain/LobbyTest.java | 3 -- 7 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java index b6d4ba47..87ee0064 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatDto.java @@ -1,7 +1,5 @@ package com.codenames.codenames.backend.chat.api.dto; -import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; - /** * Record class for messages that is used in controller. * diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java index 0d8ca3b3..0018cb28 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java @@ -1,6 +1,5 @@ package com.codenames.codenames.backend.game.domain; -import com.codenames.codenames.backend.game.domain.Color; import lombok.Getter; /** Represents a single card on a board. */ diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 1e3b5891..9f6f96cb 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -1,8 +1,5 @@ package com.codenames.codenames.backend.lobby.domain; -import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; import java.security.SecureRandom; import java.util.HashMap; import java.util.List; @@ -13,9 +10,8 @@ /** * Represents a game lobby containing a limited number of playerList. * - *

Supports adding and removing playerList - * while enforcing constraints such as maximum player count - * and unique usernames. + *

Supports adding and removing playerList while enforcing constraints such as maximum player + * count and unique usernames. */ @Getter public class Lobby { @@ -25,21 +21,18 @@ public class Lobby { private final String lobbyCode; private final List playerList = new CopyOnWriteArrayList<>(); private final SecureRandom random = new SecureRandom(); - /** - * Maps a username to the selected team. - */ + + /** Maps a username to the selected team. */ private final Map playerTeams; - /** - * Maps a username to the selected role. - */ + /** Maps a username to the selected role. */ private final Map playerRoles; /** * Creates a new lobby and adds the initial player. * * @param lobbyCode the unique code identifying the lobby - * @param username the username of the player creating the lobby + * @param username the username of the player creating the lobby */ public Lobby(String lobbyCode, String username) { this.lobbyCode = lobbyCode; @@ -52,7 +45,7 @@ public Lobby(String lobbyCode, String username) { * Adds a player to the lobby if capacity allows and the username is unique. * * @param username the username of the player - * @param isHost whether the player is the host of the lobby + * @param isHost whether the player is the host of the lobby * @return {@code true} if the player was added, {@code false} otherwise */ public boolean addPlayer(String username, boolean isHost) { @@ -67,8 +60,8 @@ public boolean addPlayer(String username, boolean isHost) { } /** - * Adds a player to the lobby if capacity allows and the username is unique. - * Calls {@link #addPlayer(String, boolean)} with {@code false} as the second argument + * Adds a player to the lobby if capacity allows and the username is unique. Calls {@link + * #addPlayer(String, boolean)} with {@code false} as the second argument * * @param username the username of the player * @return {@code true} if the player was added, {@code false} otherwise @@ -102,7 +95,7 @@ public boolean hasPlayer(String username) { * Sets the team for a player. * * @param username the username of the player - * @param team the team to assign + * @param team the team to assign */ public void setPlayerTeam(String username, Team team) { playerTeams.put(username, team); @@ -112,7 +105,7 @@ public void setPlayerTeam(String username, Team team) { * Sets the role for a player. * * @param username the username of the player - * @param role the role to assign + * @param role the role to assign */ public void setPlayerRole(String username, Role role) { playerRoles.put(username, role); diff --git a/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java index cd3cbdd5..4a042715 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/ClueValidationServiceTest.java @@ -7,7 +7,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.domain.Board; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.Color; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java index bd87fed8..2ecee413 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/BoardTest.java @@ -11,7 +11,6 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.domain.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java index 9a0be028..d4eb1d6d 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/CardTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.game.domain.Color; import org.junit.jupiter.api.Test; /** Unit tests for Card. */ diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index dfab70d6..ed520e54 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -5,8 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; import org.junit.jupiter.api.Test; /** @@ -14,7 +12,6 @@ * *

Validates player management and lobby constraints. */ - class LobbyTest { @Test From 60cb7be4423922daf60d220a7fdea0bc9e3baec8 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 20:52:36 +0200 Subject: [PATCH 016/172] refactor: remove unused import --- .../java/com/codenames/codenames/backend/game/domain/Board.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/Board.java b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java index a92940b9..ffb5ff70 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/Board.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Board.java @@ -1,7 +1,6 @@ package com.codenames.codenames.backend.game.domain; import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.domain.Color; import java.util.ArrayList; import java.util.List; import lombok.Getter; From 8eddac71f48143abb294ac5cc98d03aac8efb0d0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 20:53:57 +0200 Subject: [PATCH 017/172] refactor: fix import order --- .../codenames/codenames/backend/chat/api/ChatController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java b/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java index 64382cd9..9fc8f11c 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/ChatController.java @@ -1,9 +1,9 @@ package com.codenames.codenames.backend.chat.api; import com.codenames.codenames.backend.chat.api.dto.ChatDto; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; +import com.codenames.codenames.backend.chat.application.ChatService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.messaging.handler.annotation.DestinationVariable; From 2b545e2ea3aee1d10d8228367b2c7c61ceb99eba Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:01:28 +0200 Subject: [PATCH 018/172] refactor: change import order --- .../codenames/backend/lobby/api/LobbySocketController.java | 4 ++-- .../codenames/backend/lobby/application/LobbyService.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index ea3f7911..7d4cc8dc 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -1,9 +1,9 @@ package com.codenames.codenames.backend.lobby.api; -import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; -import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.lobby.api.dto.JoinMessage; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import java.util.List; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index 21dff21b..dd1454df 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -2,9 +2,9 @@ import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; From a43c0b3fc5743fe663c1c300dbc30d5af7abd637 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:01:52 +0200 Subject: [PATCH 019/172] refactor: change import order --- .../codenames/backend/chat/api/ChatControllerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java index 356c876b..b75dd028 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java @@ -7,12 +7,12 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.chat.api.dto.ChatDto; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.chat.application.ChatService; -import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; +import com.codenames.codenames.backend.chat.application.ChatService; +import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.shared.websocket.SessionRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; From f18d9fd3dc62a89e9c4c152d20b8312ff72b0607 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:02:21 +0200 Subject: [PATCH 020/172] refactor: 100 characters per line --- .../codenames/backend/chat/api/ChatControllerTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java index b75dd028..8fa69939 100644 --- a/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/chat/api/ChatControllerTest.java @@ -55,7 +55,8 @@ void setUp() { void testSendLobbyMessage_valid() { chatController.sendLobbyMessage(lobbyId, chatDto); - ChatDto verifiedChatDto = new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); + ChatDto verifiedChatDto = + new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); verify(chatService, times(1)).processMessage(lobbyId, "LOBBY", "", verifiedChatDto); } @@ -65,7 +66,8 @@ void testSendTeamMessage_valid() { chatController.sendTeamMessage(lobbyId, redTeam, chatDto); - ChatDto verifiedChatDto = new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); + ChatDto verifiedChatDto = + new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); verify(chatService, times(1)).processMessage(lobbyId, "TEAM_RED", "/RED", verifiedChatDto); } @@ -85,7 +87,8 @@ void testSendTeamOperativeMessage_valid() { chatController.sendTeamOperativeMessage(lobbyId, blueTeam, chatDto); - ChatDto verifiedChatDto = new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); + ChatDto verifiedChatDto = + new ChatDto(chatDto.senderUsername(), chatDto.content(), chatDto.type()); verify(chatService, times(1)) .processMessage(lobbyId, "OPERATIVE_BLUE", "/BLUE/operative", verifiedChatDto); } From 5f360e474031a0ee04c2614f735a5b05131bbd0d Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:15:27 +0200 Subject: [PATCH 021/172] refactor: change import order --- .../codenames/backend/lobby/api/LobbyControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index 565cdb67..a58c68ed 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -10,9 +10,9 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; From 32d7aa8223898eb90c9c8635088b7cedf012d4a7 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:22:01 +0200 Subject: [PATCH 022/172] refactor: change import order --- .../codenames/backend/lobby/application/LobbyServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index 66b3a426..8c187132 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -14,9 +14,9 @@ import com.codenames.codenames.backend.chat.application.ChatService; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Player; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; From 8bc71df597eeee5e51c8af04d4306b363cdcfbe0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 27 May 2026 21:26:23 +0200 Subject: [PATCH 023/172] refactor: change formatting to original format --- .../backend/serialization/SerializationJsonTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 87d64221..90356dde 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -41,9 +41,10 @@ void setUp() { @Test void testSerialize_pass() { - String expectedResult = - """ - {"winner":"RED","currentTurn":"RED","currentPhase":"SPYMASTER","currentClue":{"word":"Test","guessAmount":1},"cardList":[{"word":"TEST","color":null,"isGuessed":false}]}"""; + String expectedResult = "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," + + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," + + "\"guessAmount\":1},\"cardList\":[{\"word\":\"TEST\"," + + "\"color\":null,\"isGuessed\":false}]}"; String result = serializer.serialize(dummyGameState); assertEquals(expectedResult, result); } From dab6749c519d53bcff4ded08e59dc45043f5c690 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 00:27:58 +0200 Subject: [PATCH 024/172] feat: create lobby table --- .../resources/db/migration/V1__initial_migration_schema.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/resources/db/migration/V1__initial_migration_schema.sql diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql new file mode 100644 index 00000000..ff273513 --- /dev/null +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE lobby +( + lobby_code VARCHAR(255) PRIMARY KEY, + status VARCHAR(255) NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); From 5c744fc53991d61afb86f4e46b51defa6e1e3a9e Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 01:05:39 +0200 Subject: [PATCH 025/172] feat: add table for player We need to decide as a team if duplicates are allowed or not. For now we leave the constraint as is, can be deleted in t he future --- .../V1__initial_migration_schema.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index ff273513..8cee939b 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -1,6 +1,25 @@ +-- Name --> Type --> Default --> Nullability --> Constraint + CREATE TABLE lobby ( lobby_code VARCHAR(255) PRIMARY KEY, status VARCHAR(255) NOT NULL, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL ); + +CREATE TABLE player +( + id SERIAL PRIMARY KEY, + lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + username VARCHAR(255) NOT NULL, + is_host BOOLEAN DEFAULT FALSE NOT NULL, + team VARCHAR(255) NOT NULL, + role VARCHAR(255) NOT NULL, + + CONSTRAINT check_player_team + CHECK (team IN ('RED', 'BLUE')), + CONSTRAINT check_player_role + CHECK (role IN ('OPERATIVE', 'SPYMASTER')), + CONSTRAINT unique_player_per_lobby -- ask team if username in lobby has to be unique or not + UNIQUE (lobby_code, username) +); From 522038610bbb10ba37a347a5805b1a7f62d4ff4d Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 01:29:25 +0200 Subject: [PATCH 026/172] feat: add table for game state --- .../migration/V1__initial_migration_schema.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 8cee939b..c0291150 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -23,3 +23,21 @@ CREATE TABLE player CONSTRAINT unique_player_per_lobby -- ask team if username in lobby has to be unique or not UNIQUE (lobby_code, username) ); + +CREATE TABLE game_state +( + id SERIAL PRIMARY KEY, + lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + current_turn VARCHAR(255) NOT NULL, + current_phase VARCHAR(255) NOT NULL, + clue_word VARCHAR(255), + clue_guess_amount INT DEFAULT 0, + remaining_guesses INT DEFAULT 0, + + CONSTRAINT check_game_turn + CHECK (current_turn IN ('RED', 'BLUE')), + CONSTRAINT check_game_phase + CHECK (current_phase IN ('SPYMASTER', 'OPERATIVE')), + CONSTRAINT unique_game_per_lobby + UNIQUE (lobby_code) +); \ No newline at end of file From 63f0927e9d71ff64f379c15faa646f9f2c36cd86 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 01:40:51 +0200 Subject: [PATCH 027/172] refactor: use BIG instead of normal data types Turns out big variant is not as expensive as expected. As most systems run on 64 bit processors, it might have performance increase. Additionally (source reddit) --- .../db/migration/V1__initial_migration_schema.sql | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index c0291150..8ac2e9d8 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -2,14 +2,14 @@ CREATE TABLE lobby ( - lobby_code VARCHAR(255) PRIMARY KEY, - status VARCHAR(255) NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL + lobby_code VARCHAR(255) PRIMARY KEY, + status VARCHAR(255) NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL -- plan is to use this to delete stale lobbies ); CREATE TABLE player ( - id SERIAL PRIMARY KEY, + id BIGSERIAL PRIMARY KEY, lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, username VARCHAR(255) NOT NULL, is_host BOOLEAN DEFAULT FALSE NOT NULL, @@ -24,9 +24,10 @@ CREATE TABLE player UNIQUE (lobby_code, username) ); + CREATE TABLE game_state ( - id SERIAL PRIMARY KEY, + id BIGSERIAL PRIMARY KEY, lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, current_turn VARCHAR(255) NOT NULL, current_phase VARCHAR(255) NOT NULL, From 5cb88b2349aa2ed560dc074f0e2fdd0d342b5cd9 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 01:43:12 +0200 Subject: [PATCH 028/172] refactor: update how primary keys are created to modern standards (source CrunchyData) --- .../resources/db/migration/V1__initial_migration_schema.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 8ac2e9d8..08cc997b 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -9,7 +9,7 @@ CREATE TABLE lobby CREATE TABLE player ( - id BIGSERIAL PRIMARY KEY, + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, username VARCHAR(255) NOT NULL, is_host BOOLEAN DEFAULT FALSE NOT NULL, @@ -27,7 +27,7 @@ CREATE TABLE player CREATE TABLE game_state ( - id BIGSERIAL PRIMARY KEY, + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, current_turn VARCHAR(255) NOT NULL, current_phase VARCHAR(255) NOT NULL, From d981b20fa379a59d29cb9331caced4c690a07702 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 01:57:56 +0200 Subject: [PATCH 029/172] feat: add table for cards Note: each game will have 25 entries in the card table --- .../V1__initial_migration_schema.sql | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 08cc997b..150d8ff5 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -41,4 +41,23 @@ CREATE TABLE game_state CHECK (current_phase IN ('SPYMASTER', 'OPERATIVE')), CONSTRAINT unique_game_per_lobby UNIQUE (lobby_code) -); \ No newline at end of file +); + +-- each lobby will have 25 entries in the card table +CREATE TABLE card +( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + position INT NOT NULL, + word VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + is_guessed BOOLEAN DEFAULT FALSE NOT NULL, + + CONSTRAINT check_card_position + CHECK (position BETWEEN 0 AND 24), + CONSTRAINT check_card_color + CHECK (color IN ('RED', 'BLUE', 'NEUTRAL', 'ASSASSIN')), + CONSTRAINT unique_card_position_per_game + UNIQUE (lobby_code, position) +); + From ec81c0b38d4b28b073143c3ee8012e54a08b5d04 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 02:20:44 +0200 Subject: [PATCH 030/172] feat: add table for chat Need to discuss with team if we want to store chat in the first place. If yes, we need to consider if we want to store time stamp or not --- .../db/migration/V1__initial_migration_schema.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 150d8ff5..d26e74be 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -61,3 +61,15 @@ CREATE TABLE card UNIQUE (lobby_code, position) ); +-- debatable if we even need this, will need to ask team +-- room_key is what ChatHistory uses as the map key (e.g. "lobby", "TEAM_RED", "OPERATIVE_BLUE"). +CREATE TABLE chat_message +( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + room_key VARCHAR(255) NOT NULL, + sender_username VARCHAR(255) NOT NULL, + content TEXT NOT NULL, + message_type VARCHAR(255) NOT NULL, + sent_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, -- iirc, frontend plans on saving this?? backend might need to be refactored to store it as well if we decide to have chat logs in DB for retrieval... we would likely need to store time if we want frontend to display correct time +); \ No newline at end of file From 8d3dde39940f8d850d909c0648e1bd2b2388b0b1 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 02:32:02 +0200 Subject: [PATCH 031/172] refactor: change PK of game_state table Since it is a 1:1 relation, the id is unnecessary and lobby_code is sufficient --- .../db/migration/V1__initial_migration_schema.sql | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index d26e74be..18027cb9 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -27,8 +27,7 @@ CREATE TABLE player CREATE TABLE game_state ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + lobby_code VARCHAR(255) PRIMARY KEY REFERENCES lobby (lobby_code) ON DELETE CASCADE, current_turn VARCHAR(255) NOT NULL, current_phase VARCHAR(255) NOT NULL, clue_word VARCHAR(255), @@ -39,8 +38,6 @@ CREATE TABLE game_state CHECK (current_turn IN ('RED', 'BLUE')), CONSTRAINT check_game_phase CHECK (current_phase IN ('SPYMASTER', 'OPERATIVE')), - CONSTRAINT unique_game_per_lobby - UNIQUE (lobby_code) ); -- each lobby will have 25 entries in the card table @@ -71,5 +68,5 @@ CREATE TABLE chat_message sender_username VARCHAR(255) NOT NULL, content TEXT NOT NULL, message_type VARCHAR(255) NOT NULL, - sent_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, -- iirc, frontend plans on saving this?? backend might need to be refactored to store it as well if we decide to have chat logs in DB for retrieval... we would likely need to store time if we want frontend to display correct time + sent_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() -- iirc, frontend plans on saving this?? backend might need to be refactored to store it as well if we decide to have chat logs in DB for retrieval... we would likely need to store time if we want frontend to display correct time ); \ No newline at end of file From 844eb8142f9b3562e2df9c9c947cdca8fd926ceb Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 02:45:50 +0200 Subject: [PATCH 032/172] fix: delete stray comma --- .../resources/db/migration/V1__initial_migration_schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 18027cb9..190d90cf 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -37,7 +37,7 @@ CREATE TABLE game_state CONSTRAINT check_game_turn CHECK (current_turn IN ('RED', 'BLUE')), CONSTRAINT check_game_phase - CHECK (current_phase IN ('SPYMASTER', 'OPERATIVE')), + CHECK (current_phase IN ('SPYMASTER', 'OPERATIVE')) ); -- each lobby will have 25 entries in the card table From a3b18d62ebdc03ff797bad0f1fc7774c5c5a9f27 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 15:31:19 +0200 Subject: [PATCH 033/172] refactor: change length of varchar for DB entries While the diskspace usage stays the same for all values sub 255, the searching might suffer from using large sizes. Also security concerns/ malicious inputs/ wrong inputs can be minimized by not allowing a standard large varchar size --- .../V1__initial_migration_schema.sql | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 190d90cf..423a2a44 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -2,19 +2,18 @@ CREATE TABLE lobby ( - lobby_code VARCHAR(255) PRIMARY KEY, - status VARCHAR(255) NOT NULL, + lobby_code VARCHAR(5) PRIMARY KEY, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL -- plan is to use this to delete stale lobbies ); CREATE TABLE player ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, - username VARCHAR(255) NOT NULL, + lobby_code VARCHAR(5) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + username VARCHAR(20) NOT NULL, is_host BOOLEAN DEFAULT FALSE NOT NULL, - team VARCHAR(255) NOT NULL, - role VARCHAR(255) NOT NULL, + team VARCHAR(4) NOT NULL, + role VARCHAR(9) NOT NULL, CONSTRAINT check_player_team CHECK (team IN ('RED', 'BLUE')), @@ -27,10 +26,10 @@ CREATE TABLE player CREATE TABLE game_state ( - lobby_code VARCHAR(255) PRIMARY KEY REFERENCES lobby (lobby_code) ON DELETE CASCADE, - current_turn VARCHAR(255) NOT NULL, - current_phase VARCHAR(255) NOT NULL, - clue_word VARCHAR(255), + lobby_code VARCHAR(5) PRIMARY KEY REFERENCES lobby (lobby_code) ON DELETE CASCADE, + current_turn VARCHAR(4) NOT NULL, + current_phase VARCHAR(9) NOT NULL, + clue_word VARCHAR(20), clue_guess_amount INT DEFAULT 0, remaining_guesses INT DEFAULT 0, @@ -44,10 +43,10 @@ CREATE TABLE game_state CREATE TABLE card ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + lobby_code VARCHAR(5) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, position INT NOT NULL, - word VARCHAR(255) NOT NULL, - color VARCHAR(255) NOT NULL, + word VARCHAR(11) NOT NULL, + color VARCHAR(8) NOT NULL, is_guessed BOOLEAN DEFAULT FALSE NOT NULL, CONSTRAINT check_card_position @@ -63,10 +62,10 @@ CREATE TABLE card CREATE TABLE chat_message ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - lobby_code VARCHAR(255) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, - room_key VARCHAR(255) NOT NULL, - sender_username VARCHAR(255) NOT NULL, + lobby_code VARCHAR(5) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, + room_key VARCHAR(20) NOT NULL, + sender_username VARCHAR(20) NOT NULL, content TEXT NOT NULL, - message_type VARCHAR(255) NOT NULL, + message_type VARCHAR(10) NOT NULL, sent_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() -- iirc, frontend plans on saving this?? backend might need to be refactored to store it as well if we decide to have chat logs in DB for retrieval... we would likely need to store time if we want frontend to display correct time ); \ No newline at end of file From 4b8a45a6bf48af0805b8ef5da384e6137a5620ff Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 2 Jun 2026 20:55:00 +0200 Subject: [PATCH 034/172] chore: remove chat table, team decision --- .../db/migration/V1__initial_migration_schema.sql | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/resources/db/migration/V1__initial_migration_schema.sql b/src/main/resources/db/migration/V1__initial_migration_schema.sql index 423a2a44..44e624c9 100644 --- a/src/main/resources/db/migration/V1__initial_migration_schema.sql +++ b/src/main/resources/db/migration/V1__initial_migration_schema.sql @@ -55,17 +55,4 @@ CREATE TABLE card CHECK (color IN ('RED', 'BLUE', 'NEUTRAL', 'ASSASSIN')), CONSTRAINT unique_card_position_per_game UNIQUE (lobby_code, position) -); - --- debatable if we even need this, will need to ask team --- room_key is what ChatHistory uses as the map key (e.g. "lobby", "TEAM_RED", "OPERATIVE_BLUE"). -CREATE TABLE chat_message -( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - lobby_code VARCHAR(5) NOT NULL REFERENCES lobby (lobby_code) ON DELETE CASCADE, - room_key VARCHAR(20) NOT NULL, - sender_username VARCHAR(20) NOT NULL, - content TEXT NOT NULL, - message_type VARCHAR(10) NOT NULL, - sent_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() -- iirc, frontend plans on saving this?? backend might need to be refactored to store it as well if we decide to have chat logs in DB for retrieval... we would likely need to store time if we want frontend to display correct time ); \ No newline at end of file From fa47584dfb1971b12fc91ab58c94a40c804de466 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 14:53:40 +0200 Subject: [PATCH 035/172] feat: add player entity class --- .../codenames/backend/database/Player.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/Player.java diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/Player.java new file mode 100644 index 00000000..15455431 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/Player.java @@ -0,0 +1,32 @@ +package com.codenames.codenames.backend.database; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "player") +public class Player { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "lobby_code", nullable = false) + private String lobbyCode; + @Column(nullable = false) + private String username; + @Column(name = "is_host", nullable = false) + private Boolean isHost; + @Column(nullable = false) + private String team; + @Column(nullable = false) + private String role; +} From 3bddc601c39e4735042e8a9305a7785828ac594c Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:05:18 +0200 Subject: [PATCH 036/172] feat: add length limit to player table columns --- .../com/codenames/codenames/backend/database/Player.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/Player.java index 15455431..1aa41c2b 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/Player.java @@ -19,14 +19,14 @@ public class Player { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "lobby_code", nullable = false) + @Column(name = "lobby_code", length = 5, nullable = false) private String lobbyCode; - @Column(nullable = false) + @Column(length = 20, nullable = false) private String username; @Column(name = "is_host", nullable = false) private Boolean isHost; - @Column(nullable = false) + @Column(length = 4, nullable = false) private String team; - @Column(nullable = false) + @Column(length = 9, nullable = false) private String role; } From 11aef64ee2dd6d846be2136f120948612ebd9190 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:08:25 +0200 Subject: [PATCH 037/172] feat: add lobby entity class --- .../codenames/backend/database/Lobby.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/Lobby.java diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/Lobby.java new file mode 100644 index 00000000..bfbafc8a --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/Lobby.java @@ -0,0 +1,22 @@ +package com.sample; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "lobby") +public class Lobby { + @Id + @Column(name = "lobby_code", length = 5, nullable = false) + private String lobbyCode; + @Column(name = "created_at", insertable = false, updatable = false, nullable = false) + private java.sql.Timestamp createdAt; +} From 0cb958e4d26eb3a556bc1fc5933a6f43e60a3ba0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:11:41 +0200 Subject: [PATCH 038/172] feat: add game state entity class --- .../codenames/backend/database/GameState.java | 32 +++++++++++++++++++ .../codenames/backend/database/Lobby.java | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/codenames/codenames/backend/database/GameState.java diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java new file mode 100644 index 00000000..8aff64eb --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -0,0 +1,32 @@ +package com.codenames.codenames.backend.database; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "game_state") +public class GameState { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private String lobbyCode; + @Column(name = "current_turn", length = 4, nullable = false) + private String currentTurn; + @Column(name = "current_phase", length = 9, nullable = false) + private String currentPhase; + @Column(name = "clue_word", length = 20) + private String clueWord; + @Column(name = "clue_guess_amount") + private long clueGuessAmount; + @Column(name = "remaining_guesses") + private long remainingGuesses; +} diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/Lobby.java index bfbafc8a..24f61689 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/Lobby.java @@ -1,4 +1,4 @@ -package com.sample; +package com.codenames.codenames.backend.database; import jakarta.persistence.Column; import jakarta.persistence.Entity; From 19d48a2098a57852e8d189be9c1e89ebc001c685 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:14:43 +0200 Subject: [PATCH 039/172] feat: add card entity class --- .../codenames/backend/database/Card.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/Card.java diff --git a/src/main/java/com/codenames/codenames/backend/database/Card.java b/src/main/java/com/codenames/codenames/backend/database/Card.java new file mode 100644 index 00000000..a9d32f20 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/Card.java @@ -0,0 +1,32 @@ +package com.codenames.codenames.backend.database; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "card") +public class Card { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + @Column(name = "lobby_code", length = 5, nullable = false) + private String lobbyCode; + @Column(nullable = false) + private int position; + @Column(length = 11, nullable = false) + private String word; + @Column(length = 8, nullable = false) + private String color; + @Column(name = "is_guessed", nullable = false) + private Boolean isGuessed; +} From d58db42277a8b41f4fbfb74efe6b9b115c66af91 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:27:33 +0200 Subject: [PATCH 040/172] docs: add javaDocs --- .../java/com/codenames/codenames/backend/database/Card.java | 3 +++ .../com/codenames/codenames/backend/database/GameState.java | 3 +++ .../java/com/codenames/codenames/backend/database/Lobby.java | 3 +++ .../java/com/codenames/codenames/backend/database/Player.java | 3 +++ 4 files changed, 12 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/Card.java b/src/main/java/com/codenames/codenames/backend/database/Card.java index a9d32f20..b9ccd1ae 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Card.java +++ b/src/main/java/com/codenames/codenames/backend/database/Card.java @@ -10,6 +10,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +/** + * Persistent entity representing a card record within the database schema. + */ @Getter @Setter @NoArgsConstructor diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java index 8aff64eb..be766694 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -10,6 +10,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +/** + * Persistent entity representing a gameState record within the database schema. + */ @Getter @Setter @NoArgsConstructor diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/Lobby.java index 24f61689..18672977 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/Lobby.java @@ -8,6 +8,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +/** + * Persistent entity representing a lobby record within the database schema. + */ @Getter @Setter @NoArgsConstructor diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/Player.java index 1aa41c2b..251263f2 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/Player.java @@ -10,6 +10,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +/** + * Persistent entity representing a player record within the database schema. + */ @Getter @Setter @NoArgsConstructor From 2b8770e8d16f152ee7a6cb503a96dc9376e29ad3 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:32:33 +0200 Subject: [PATCH 041/172] fix: change types to match DB schema --- .../java/com/codenames/codenames/backend/database/Card.java | 2 +- .../com/codenames/codenames/backend/database/GameState.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/Card.java b/src/main/java/com/codenames/codenames/backend/database/Card.java index b9ccd1ae..09307cfe 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Card.java +++ b/src/main/java/com/codenames/codenames/backend/database/Card.java @@ -21,7 +21,7 @@ public class Card { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private long id; + private Long id; @Column(name = "lobby_code", length = 5, nullable = false) private String lobbyCode; @Column(nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java index be766694..7faaf776 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -29,7 +29,7 @@ public class GameState { @Column(name = "clue_word", length = 20) private String clueWord; @Column(name = "clue_guess_amount") - private long clueGuessAmount; + private int clueGuessAmount; @Column(name = "remaining_guesses") - private long remainingGuesses; + private int remainingGuesses; } From 995d94a8bcef31bdfb0d2ac223863e2367d75178 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:33:17 +0200 Subject: [PATCH 042/172] refactor: change IDE generated POJO column to proper import --- .../java/com/codenames/codenames/backend/database/Lobby.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/Lobby.java index 18672977..479a4bf1 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/Lobby.java @@ -4,6 +4,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; +import java.sql.Timestamp; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -21,5 +22,5 @@ public class Lobby { @Column(name = "lobby_code", length = 5, nullable = false) private String lobbyCode; @Column(name = "created_at", insertable = false, updatable = false, nullable = false) - private java.sql.Timestamp createdAt; + private Timestamp createdAt; } From 243c7a6861ca2ec4bf83d4bb9dde09f83220e9dd Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 15:33:40 +0200 Subject: [PATCH 043/172] fix: remove erroneous copy paste from different file --- .../java/com/codenames/codenames/backend/database/GameState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java index 7faaf776..325180b3 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -20,7 +20,6 @@ @Table(name = "game_state") public class GameState { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) private String lobbyCode; @Column(name = "current_turn", length = 4, nullable = false) private String currentTurn; From 98b70b1c63c102371e04a9669c520860b69eafc5 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 16:55:03 +0200 Subject: [PATCH 044/172] feat: add relational mappings to entity classes --- .../codenames/codenames/backend/database/Card.java | 7 +++++-- .../codenames/backend/database/GameState.java | 8 ++++++++ .../codenames/backend/database/Lobby.java | 14 ++++++++++++++ .../codenames/backend/database/Player.java | 11 +++++++++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/Card.java b/src/main/java/com/codenames/codenames/backend/database/Card.java index 09307cfe..c1d6bcde 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Card.java +++ b/src/main/java/com/codenames/codenames/backend/database/Card.java @@ -5,6 +5,8 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,8 +24,9 @@ public class Card { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "lobby_code", length = 5, nullable = false) - private String lobbyCode; + @ManyToOne + @JoinColumn(name = "lobby_code", nullable = false) + private Lobby lobby; @Column(nullable = false) private int position; @Column(length = 11, nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java index 325180b3..cb2af563 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -5,6 +5,9 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,7 +23,12 @@ @Table(name = "game_state") public class GameState { @Id + @Column(name = "lobby_code") private String lobbyCode; + @OneToOne + @MapsId + @JoinColumn(name = "lobby_code") + private Lobby lobby; @Column(name = "current_turn", length = 4, nullable = false) private String currentTurn; @Column(name = "current_phase", length = 9, nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/Lobby.java index 479a4bf1..9f615d2d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/Lobby.java @@ -1,10 +1,15 @@ package com.codenames.codenames.backend.database; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -23,4 +28,13 @@ public class Lobby { private String lobbyCode; @Column(name = "created_at", insertable = false, updatable = false, nullable = false) private Timestamp createdAt; + + @OneToOne(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) + private GameState gameState; + + @OneToMany(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) + private List players = new ArrayList<>(); + + @OneToMany(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) + private List cards = new ArrayList<>(); } diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/Player.java index 251263f2..6d5e78e2 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/Player.java @@ -2,9 +2,15 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; @@ -22,8 +28,9 @@ public class Player { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "lobby_code", length = 5, nullable = false) - private String lobbyCode; + @ManyToOne + @JoinColumn(name = "lobby_code", nullable = false) + private Lobby lobby; @Column(length = 20, nullable = false) private String username; @Column(name = "is_host", nullable = false) From 12457d408b1c86e5d6067fc567c800109712dc5f Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 17:11:25 +0200 Subject: [PATCH 045/172] chore: add lombok config to exclude generated getter setter from test --- lombok.config | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 lombok.config diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..080f8789 --- /dev/null +++ b/lombok.config @@ -0,0 +1,6 @@ +#Source - https://stackoverflow.com/a/48971823 +#Posted by mladzo, modified by community. See post 'Timeline' for change history +#Retrieved 2026-06-04, License - CC BY-SA 3.0 + +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true From e7b446b8e5dd34ef12dbd26317e1f5ec9687bec1 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 4 Jun 2026 17:14:52 +0200 Subject: [PATCH 046/172] refactor: remove unused imports --- .../com/codenames/codenames/backend/database/GameState.java | 2 -- .../java/com/codenames/codenames/backend/database/Player.java | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/GameState.java index cb2af563..e217a765 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/GameState.java @@ -2,8 +2,6 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.MapsId; diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/Player.java index 6d5e78e2..d08dc911 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/Player.java @@ -2,15 +2,11 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; -import jakarta.persistence.MapsId; -import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; From acae16f4ff3a3f825f1a454871cc1d5af7116251 Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 5 Jun 2026 15:33:11 +0200 Subject: [PATCH 047/172] feat: add repository class for lobby --- .../backend/database/repository/LobbyRepository.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java new file mode 100644 index 00000000..ea8c4f54 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java @@ -0,0 +1,11 @@ +package com.codenames.codenames.backend.database.repository; + +import com.codenames.codenames.backend.database.entity.Lobby; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository class for Lobby that has the default available methods needed to perform SQL queries. + */ +@Repository +public interface LobbyRepository extends JpaRepository {} From 34fdee53f0adfaf7eadc6a6d74f177deeaf1f80c Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 5 Jun 2026 15:41:29 +0200 Subject: [PATCH 048/172] feat: add repository class for Player Add additional query to find player in specific lobby --- .../database/repository/PlayerRepository.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java new file mode 100644 index 00000000..4aa20c23 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -0,0 +1,22 @@ +package com.codenames.codenames.backend.database.repository; + +import com.codenames.codenames.backend.database.entity.Player; +import org.springframework.stereotype.Repository; + +/** + * Repository class for Lobby that has the default available methods needed to perform SQL queries. + */ +@Repository +public interface PlayerRepository extends JpaRepository { + + /** + * Custom SQL query that is not included in the JpaRepository interface. + * + * @param lobbyCode the 5 character lobby code we want to search the player in + * @param username the username of the player we want to search for + * @return the player + */ + // First Lobby is the Lobby object inside the entity where we can find the lobby_code String. + // And "resets" the SQL search back to the root table Player + Optional findByLobbyLobbyCodeAndUsername(String lobbyCode, String username); +} From e3b4024ced4f3d1065b909221656851c84ded57c Mon Sep 17 00:00:00 2001 From: Anna Pschernig Date: Fri, 5 Jun 2026 19:30:18 +0200 Subject: [PATCH 049/172] task: added server logs to services --- .../codenames/backend/game/application/GameService.java | 4 ++++ .../backend/game/mapping/DataTransferObjectService.java | 5 +++++ .../recovery/application/SystemStatePersistenceService.java | 3 +++ .../recovery/application/SystemStateRecoveryService.java | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index 961bf26f..cdb57463 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -8,6 +8,7 @@ import com.codenames.codenames.backend.lobby.domain.Team; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** @@ -15,6 +16,7 @@ * exposes the methods of the GameManager, so that the websocket controllers can have message * mappings to allow frontend to interact with the backend. */ +@Slf4j @Service public class GameService { @@ -41,6 +43,7 @@ public GameService(GameManagerFactory gameManagerFactory, DataTransferObjectServ */ public void createGameManager(String lobbyCode, Team startingTeam) { games.computeIfAbsent(lobbyCode, key -> gameManagerFactory.create(startingTeam)); + log.info("{}: a new game manager has been created.", lobbyCode); } /** @@ -50,6 +53,7 @@ public void createGameManager(String lobbyCode, Team startingTeam) { */ public void removeGame(String lobbyCode) { games.remove(lobbyCode); + log.info("{}: Game has been removed from list.", lobbyCode); } /** diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index 453170aa..a6b0c598 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -10,9 +10,11 @@ import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** Service to create the DTO for the game state, which is then serialized into JSON. */ +@Slf4j @Service public class DataTransferObjectService { @@ -42,6 +44,9 @@ public GameStateDto createGameStateDto( for (Card card : cardList) { cardDataTransferObject.add(createCardDto(card)); } + if (gameManager.getWinner() != null) { + log.info("A Game is over. Winner is {}.", gameManager.getWinner()); + } if (gameManager.getCurrentClue() == null) { return new GameStateDto( gameManager.getWinner(), diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java index 3b27f6dc..04b017b5 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java @@ -4,6 +4,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** @@ -12,6 +13,7 @@ *

Collects lobby and game snapshots and stores them through the configured {@link * JsonStateStore} to enable recovery after application restart. */ +@Slf4j @Service public class SystemStatePersistenceService { @@ -49,5 +51,6 @@ public void persistCurrentState() { gameService.getGameSnapshots()); stateStore.save(snapshot); + log.info("Saving a new system snapshot."); } } diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index dfc7d653..4c4bf198 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -60,6 +60,9 @@ public void recoverOnStartup() { } restoreLobbies(snapshot.lobbies()); restoreGames(snapshot.games()); + log.info("Restoring of {} lobbies and {} games successful.", + snapshot.lobbies().size(), snapshot.games().size() + ); }); } @@ -70,6 +73,7 @@ public void recoverOnStartup() { */ private void restoreLobbies(Map> lobbySnapshots) { if (lobbySnapshots == null || lobbySnapshots.isEmpty()) { + log.info("There were no lobbies to restore."); return; } for (Map.Entry> entry : lobbySnapshots.entrySet()) { @@ -87,6 +91,7 @@ private void restoreLobbies(Map> lobbySnapshots) { */ private void restoreGames(Map gameSnapshots) { if (gameSnapshots == null || gameSnapshots.isEmpty()) { + log.info("There were no games to restore."); return; } for (Map.Entry entry : gameSnapshots.entrySet()) { @@ -115,6 +120,7 @@ private Lobby buildLobby(String lobbyCode, List players) { .toList(); if (validPlayers.isEmpty()) { + log.warn("Skipping restore for lobby {} due to invalid player data.", lobbyCode); return null; } From f52b5768617723488d777cd4e5f98d11c9a96f63 Mon Sep 17 00:00:00 2001 From: Anna Pschernig Date: Fri, 5 Jun 2026 19:43:23 +0200 Subject: [PATCH 050/172] fix: added null check to logs --- .../recovery/application/SystemStateRecoveryService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java index 4c4bf198..89afbdf9 100644 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java @@ -60,8 +60,10 @@ public void recoverOnStartup() { } restoreLobbies(snapshot.lobbies()); restoreGames(snapshot.games()); + int lobbyCount = (snapshot.lobbies() == null) ? 0 : snapshot.lobbies().size(); + int gameCount = (snapshot.games() == null) ? 0 : snapshot.games().size(); log.info("Restoring of {} lobbies and {} games successful.", - snapshot.lobbies().size(), snapshot.games().size() + lobbyCount, gameCount ); }); } From 11a93a6e3f3b58059e1c4b2b6e86346a2fc1b803 Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 5 Jun 2026 22:23:15 +0200 Subject: [PATCH 051/172] chore: move entity classes into dedicated package --- .../codenames/codenames/backend/database/{ => entity}/Card.java | 2 +- .../codenames/backend/database/{ => entity}/GameState.java | 2 +- .../codenames/backend/database/{ => entity}/Lobby.java | 2 +- .../codenames/backend/database/{ => entity}/Player.java | 2 +- .../codenames/backend/database/repository/PlayerRepository.java | 2 ++ 5 files changed, 6 insertions(+), 4 deletions(-) rename src/main/java/com/codenames/codenames/backend/database/{ => entity}/Card.java (94%) rename src/main/java/com/codenames/codenames/backend/database/{ => entity}/GameState.java (94%) rename src/main/java/com/codenames/codenames/backend/database/{ => entity}/Lobby.java (95%) rename src/main/java/com/codenames/codenames/backend/database/{ => entity}/Player.java (94%) diff --git a/src/main/java/com/codenames/codenames/backend/database/Card.java b/src/main/java/com/codenames/codenames/backend/database/entity/Card.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/Card.java rename to src/main/java/com/codenames/codenames/backend/database/entity/Card.java index c1d6bcde..af8ccd56 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Card.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/Card.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.database; +package com.codenames.codenames.backend.database.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/codenames/codenames/backend/database/GameState.java b/src/main/java/com/codenames/codenames/backend/database/entity/GameState.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/GameState.java rename to src/main/java/com/codenames/codenames/backend/database/entity/GameState.java index e217a765..decf5a2d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/GameState.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.database; +package com.codenames.codenames.backend.database.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/codenames/codenames/backend/database/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java similarity index 95% rename from src/main/java/com/codenames/codenames/backend/database/Lobby.java rename to src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java index 9f615d2d..17058188 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.database; +package com.codenames.codenames.backend.database.entity; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; diff --git a/src/main/java/com/codenames/codenames/backend/database/Player.java b/src/main/java/com/codenames/codenames/backend/database/entity/Player.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/Player.java rename to src/main/java/com/codenames/codenames/backend/database/entity/Player.java index d08dc911..e9fc960f 100644 --- a/src/main/java/com/codenames/codenames/backend/database/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/Player.java @@ -1,4 +1,4 @@ -package com.codenames.codenames.backend.database; +package com.codenames.codenames.backend.database.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 4aa20c23..8a73079f 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -1,6 +1,8 @@ package com.codenames.codenames.backend.database.repository; import com.codenames.codenames.backend.database.entity.Player; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; /** From a1dd4019e664ab54a75f494b0a857b35f70740af Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 5 Jun 2026 23:38:49 +0200 Subject: [PATCH 052/172] fix: change parameter to Long as player table uses BIGINT as the PK --- .../codenames/backend/database/repository/PlayerRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 8a73079f..3b2b050f 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -9,7 +9,7 @@ * Repository class for Lobby that has the default available methods needed to perform SQL queries. */ @Repository -public interface PlayerRepository extends JpaRepository { +public interface PlayerRepository extends JpaRepository { /** * Custom SQL query that is not included in the JpaRepository interface. From 699bbb6076e4c9679c7ba83f374c49d38bbd1e82 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 00:31:35 +0200 Subject: [PATCH 053/172] feat: add incomplete mapping class to store all entries for DB --- .../persistence/PersistenceMapper.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java new file mode 100644 index 00000000..c61a0550 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -0,0 +1,31 @@ +package com.codenames.codenames.backend.database.persistence; + +import com.codenames.codenames.backend.database.Card; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.database.GameState; +import com.codenames.codenames.backend.database.Lobby; +import com.codenames.codenames.backend.database.Player; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import java.util.List; + +public class PersistenceMapper { + // Should be possible to just call save on Lobby object due to cascade. Will find out in the + // future when this feature/ ticket is finished. + public Lobby mapLobbyEntity(String lobbyCode, GameManager gameManager, List players) { + Lobby lobby = new Lobby(); + lobby.setLobbyCode(lobbyCode); + + return lobby; + } + private GameState mapGameState(GameState gameState) { + return gameState; + } + + private Card mapCard(List cards) { + return card; + } + private Player mapPlayer(List players) { + + } +} From bcf9dbdcf2ef4331829e33546a183e90b759b1ef Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 00:36:10 +0200 Subject: [PATCH 054/172] refactor: rename classes -> improve maintainability --- .../database/entity/{Card.java => CardEntity.java} | 4 ++-- .../{GameState.java => GameStateEntity.java} | 4 ++-- .../entity/{Lobby.java => LobbyEntity.java} | 14 +++++++------- .../entity/{Player.java => PlayerEntity.java} | 4 ++-- .../database/repository/LobbyRepository.java | 4 ++-- .../database/repository/PlayerRepository.java | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) rename src/main/java/com/codenames/codenames/backend/database/entity/{Card.java => CardEntity.java} (94%) rename src/main/java/com/codenames/codenames/backend/database/entity/{GameState.java => GameStateEntity.java} (94%) rename src/main/java/com/codenames/codenames/backend/database/entity/{Lobby.java => LobbyEntity.java} (64%) rename src/main/java/com/codenames/codenames/backend/database/entity/{Player.java => PlayerEntity.java} (94%) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/Card.java b/src/main/java/com/codenames/codenames/backend/database/entity/CardEntity.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/entity/Card.java rename to src/main/java/com/codenames/codenames/backend/database/entity/CardEntity.java index af8ccd56..700afe32 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/Card.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/CardEntity.java @@ -20,13 +20,13 @@ @NoArgsConstructor @Entity @Table(name = "card") -public class Card { +public class CardEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "lobby_code", nullable = false) - private Lobby lobby; + private LobbyEntity lobbyEntity; @Column(nullable = false) private int position; @Column(length = 11, nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/GameState.java b/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/entity/GameState.java rename to src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java index decf5a2d..ee0832bb 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/GameState.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java @@ -19,14 +19,14 @@ @NoArgsConstructor @Entity @Table(name = "game_state") -public class GameState { +public class GameStateEntity { @Id @Column(name = "lobby_code") private String lobbyCode; @OneToOne @MapsId @JoinColumn(name = "lobby_code") - private Lobby lobby; + private LobbyEntity lobbyEntity; @Column(name = "current_turn", length = 4, nullable = false) private String currentTurn; @Column(name = "current_phase", length = 9, nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java b/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java similarity index 64% rename from src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java rename to src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java index 17058188..0b940a8b 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java @@ -22,19 +22,19 @@ @NoArgsConstructor @Entity @Table(name = "lobby") -public class Lobby { +public class LobbyEntity { @Id @Column(name = "lobby_code", length = 5, nullable = false) private String lobbyCode; @Column(name = "created_at", insertable = false, updatable = false, nullable = false) private Timestamp createdAt; - @OneToOne(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) - private GameState gameState; + @OneToOne(mappedBy = "lobbyEntity", cascade = CascadeType.ALL, orphanRemoval = true) + private GameStateEntity gameStateEntity; - @OneToMany(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) - private List players = new ArrayList<>(); + @OneToMany(mappedBy = "lobbyEntity", cascade = CascadeType.ALL, orphanRemoval = true) + private List playerEntities = new ArrayList<>(); - @OneToMany(mappedBy = "lobby", cascade = CascadeType.ALL, orphanRemoval = true) - private List cards = new ArrayList<>(); + @OneToMany(mappedBy = "lobbyEntity", cascade = CascadeType.ALL, orphanRemoval = true) + private List cardEntities = new ArrayList<>(); } diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/Player.java b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java similarity index 94% rename from src/main/java/com/codenames/codenames/backend/database/entity/Player.java rename to src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java index e9fc960f..a8f319ea 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/Player.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java @@ -20,13 +20,13 @@ @NoArgsConstructor @Entity @Table(name = "player") -public class Player { +public class PlayerEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "lobby_code", nullable = false) - private Lobby lobby; + private LobbyEntity lobbyEntity; @Column(length = 20, nullable = false) private String username; @Column(name = "is_host", nullable = false) diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java index ea8c4f54..31128aae 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/LobbyRepository.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.database.repository; -import com.codenames.codenames.backend.database.entity.Lobby; +import com.codenames.codenames.backend.database.entity.LobbyEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -8,4 +8,4 @@ * Repository class for Lobby that has the default available methods needed to perform SQL queries. */ @Repository -public interface LobbyRepository extends JpaRepository {} +public interface LobbyRepository extends JpaRepository {} diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 3b2b050f..0d1b2766 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -1,6 +1,6 @@ package com.codenames.codenames.backend.database.repository; -import com.codenames.codenames.backend.database.entity.Player; +import com.codenames.codenames.backend.database.entity.PlayerEntity; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -9,7 +9,7 @@ * Repository class for Lobby that has the default available methods needed to perform SQL queries. */ @Repository -public interface PlayerRepository extends JpaRepository { +public interface PlayerRepository extends JpaRepository { /** * Custom SQL query that is not included in the JpaRepository interface. @@ -20,5 +20,5 @@ public interface PlayerRepository extends JpaRepository { */ // First Lobby is the Lobby object inside the entity where we can find the lobby_code String. // And "resets" the SQL search back to the root table Player - Optional findByLobbyLobbyCodeAndUsername(String lobbyCode, String username); + Optional findByLobbyLobbyCodeAndUsername(String lobbyCode, String username); } From 159c5577248543f8efe40d85ba1cf7ff69d40fde Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 00:40:08 +0200 Subject: [PATCH 055/172] fix: update imports after merge and incorrect parameters --- .../persistence/PersistenceMapper.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index c61a0550..8b3c40eb 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -1,10 +1,11 @@ package com.codenames.codenames.backend.database.persistence; -import com.codenames.codenames.backend.database.Card; +import com.codenames.codenames.backend.database.entity.CardEntity; +import com.codenames.codenames.backend.database.entity.GameStateEntity; +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.database.GameState; -import com.codenames.codenames.backend.database.Lobby; -import com.codenames.codenames.backend.database.Player; + import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import java.util.List; @@ -12,20 +13,18 @@ public class PersistenceMapper { // Should be possible to just call save on Lobby object due to cascade. Will find out in the // future when this feature/ ticket is finished. - public Lobby mapLobbyEntity(String lobbyCode, GameManager gameManager, List players) { - Lobby lobby = new Lobby(); + public LobbyEntity mapLobbyEntity(String lobbyCode, GameManager gameManager, List players) { + LobbyEntity lobby = new LobbyEntity(); lobby.setLobbyCode(lobbyCode); return lobby; } - private GameState mapGameState(GameState gameState) { - return gameState; + private GameStateEntity mapGameState(GameManager gameManager) { } - private Card mapCard(List cards) { - return card; + private CardEntity mapCard(List cards) { } - private Player mapPlayer(List players) { + private PlayerEntity mapPlayer(List players) { } } From e4297dac6b5a08d1025dd1c216ff5a4895a6850c Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 11:29:29 +0200 Subject: [PATCH 056/172] fix: rename method to match entity class --- .../codenames/backend/database/repository/PlayerRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 0d1b2766..6bb4a82c 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -20,5 +20,5 @@ public interface PlayerRepository extends JpaRepository { */ // First Lobby is the Lobby object inside the entity where we can find the lobby_code String. // And "resets" the SQL search back to the root table Player - Optional findByLobbyLobbyCodeAndUsername(String lobbyCode, String username); + Optional findByLobbyEntityLobbyCodeAndUsername(String lobbyCode, String username); } From 6053b7ba33a3d322bb538cf24622d36796331cd7 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 12:00:06 +0200 Subject: [PATCH 057/172] feat: add mapping for gameState table --- .../persistence/PersistenceMapper.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 8b3c40eb..9c5233cf 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -13,18 +13,31 @@ public class PersistenceMapper { // Should be possible to just call save on Lobby object due to cascade. Will find out in the // future when this feature/ ticket is finished. - public LobbyEntity mapLobbyEntity(String lobbyCode, GameManager gameManager, List players) { - LobbyEntity lobby = new LobbyEntity(); - lobby.setLobbyCode(lobbyCode); + public LobbyEntity mapLobbyEntity( + String lobbyCode, GameManager gameManager, List players) { + LobbyEntity lobbyEntity = new LobbyEntity(); + lobbyEntity.setLobbyCode(lobbyCode); - return lobby; - } - private GameStateEntity mapGameState(GameManager gameManager) { - } + lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); - private CardEntity mapCard(List cards) { + return lobbyEntity; } - private PlayerEntity mapPlayer(List players) { + private GameStateEntity mapGameState( + LobbyEntity lobbyEntity, String lobbyCode, GameManager gameManager) { + GameStateEntity gameStateEntity = new GameStateEntity(); + // Since we are using ORM we need to map the object as well. + gameStateEntity.setLobbyEntity(lobbyEntity); + gameStateEntity.setLobbyCode(lobbyCode); + gameStateEntity.setCurrentTurn(gameManager.getCurrentTurn().name()); + gameStateEntity.setCurrentPhase(gameManager.getCurrentPhase().name()); + gameStateEntity.setClueWord(gameManager.getCurrentClueWord()); + gameStateEntity.setClueGuessAmount(gameStateEntity.getClueGuessAmount()); + gameStateEntity.setRemainingGuesses(gameStateEntity.getRemainingGuesses()); + return gameStateEntity; } + + private CardEntity mapCard(List cards) {} + + private PlayerEntity mapPlayer(List players) {} } From 9bc04a05e47dce575b65e95ec6cf5d0e0ce27319 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 12:26:35 +0200 Subject: [PATCH 058/172] feat: add mapping for card table --- .../persistence/PersistenceMapper.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 9c5233cf..7dee957e 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -8,6 +8,7 @@ import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import java.util.ArrayList; import java.util.List; public class PersistenceMapper { @@ -19,6 +20,9 @@ public LobbyEntity mapLobbyEntity( lobbyEntity.setLobbyCode(lobbyCode); lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); + List cardList = gameManager.getCardList(); + lobbyEntity.setCardEntities(mapCard(lobbyEntity, lobbyCode, cardList)); + return lobbyEntity; } @@ -37,7 +41,19 @@ private GameStateEntity mapGameState( return gameStateEntity; } - private CardEntity mapCard(List cards) {} + private List mapCard(LobbyEntity lobbyEntity, String lobbyCode, List cards) { + List cardList = new ArrayList<>(); + for(int i = 0; i < cards.size(); i++) { + CardEntity cardEntity = new CardEntity(); + cardEntity.setLobbyEntity(lobbyEntity); + cardEntity.setPosition(i); + cardEntity.setWord(cards.get(i).getWord()); + cardEntity.setColor(cards.get(i).getColor().name()); + cardEntity.setIsGuessed(cards.get(i).isGuessed()); + cardList.add(cardEntity); + } + return cardList; + } private PlayerEntity mapPlayer(List players) {} } From f55f613d53ec2d29c881cdb43035583a9adb1a7e Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 12:36:35 +0200 Subject: [PATCH 059/172] feat: add player table mapping --- .../persistence/PersistenceMapper.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 7dee957e..c78cf9a9 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -14,24 +14,45 @@ public class PersistenceMapper { // Should be possible to just call save on Lobby object due to cascade. Will find out in the // future when this feature/ ticket is finished. - public LobbyEntity mapLobbyEntity( + public LobbyEntity mapAggregateParentLobbyEntity( String lobbyCode, GameManager gameManager, List players) { + LobbyEntity lobbyEntity = new LobbyEntity(); lobbyEntity.setLobbyCode(lobbyCode); - lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); List cardList = gameManager.getCardList(); - lobbyEntity.setCardEntities(mapCard(lobbyEntity, lobbyCode, cardList)); + lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); + lobbyEntity.setCardEntities(mapCard(lobbyEntity, lobbyCode, cardList)); + lobbyEntity.setPlayerEntities(mapPlayer(lobbyEntity, lobbyCode, players)); return lobbyEntity; } + private List mapPlayer( + LobbyEntity lobbyEntity, String lobbyCode, List players) { + List playerList = new ArrayList<>(); + + for (int i = 0; i < players.size(); i++) { + PlayerEntity playerEntity = new PlayerEntity(); + playerEntity.setLobbyEntity(lobbyEntity); + playerEntity.setUsername(players.get(i).username()); + playerEntity.setIsHost(players.get(i).isHost()); + playerEntity.setTeam(players.get(i).team().name()); + playerEntity.setRole(players.get(i).role().name()); + playerList.add(playerEntity); + } + return playerList; + } + + // Since we are using ORM we always need to map the object as well. + + private GameStateEntity mapGameState( LobbyEntity lobbyEntity, String lobbyCode, GameManager gameManager) { GameStateEntity gameStateEntity = new GameStateEntity(); - // Since we are using ORM we need to map the object as well. gameStateEntity.setLobbyEntity(lobbyEntity); + // Since we do not have autogenerating PK we need to manually map. gameStateEntity.setLobbyCode(lobbyCode); gameStateEntity.setCurrentTurn(gameManager.getCurrentTurn().name()); gameStateEntity.setCurrentPhase(gameManager.getCurrentPhase().name()); @@ -43,7 +64,7 @@ private GameStateEntity mapGameState( private List mapCard(LobbyEntity lobbyEntity, String lobbyCode, List cards) { List cardList = new ArrayList<>(); - for(int i = 0; i < cards.size(); i++) { + for (int i = 0; i < cards.size(); i++) { CardEntity cardEntity = new CardEntity(); cardEntity.setLobbyEntity(lobbyEntity); cardEntity.setPosition(i); @@ -54,6 +75,4 @@ private List mapCard(LobbyEntity lobbyEntity, String lobbyCode, List } return cardList; } - - private PlayerEntity mapPlayer(List players) {} } From ffb76f8d2e974d48ca74506481581fd30f71e3cb Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 14:20:38 +0200 Subject: [PATCH 060/172] docs: add javaDocs --- .../database/persistence/PersistenceMapper.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index c78cf9a9..38b0e83d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -11,9 +11,22 @@ import java.util.ArrayList; import java.util.List; +/** + * Helper class that is called by the PersistenceService to map all the tables when a snapshot is + * created. + */ public class PersistenceMapper { // Should be possible to just call save on Lobby object due to cascade. Will find out in the // future when this feature/ ticket is finished. + + /** + * This method creates and returns a lobby entity that is to be stored into the DB. + * + * @param lobbyCode the code to act as PK and FK for other tables + * @param gameManager the gameManager of an active game to derive information for other tables + * @param players the list of players in an active game + * @return the lobby entity to be stored into dp + */ public LobbyEntity mapAggregateParentLobbyEntity( String lobbyCode, GameManager gameManager, List players) { @@ -47,7 +60,6 @@ private List mapPlayer( // Since we are using ORM we always need to map the object as well. - private GameStateEntity mapGameState( LobbyEntity lobbyEntity, String lobbyCode, GameManager gameManager) { GameStateEntity gameStateEntity = new GameStateEntity(); From 4a30f094099795836491eb0dc0c62b55a4fdf4bc Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 14:35:50 +0200 Subject: [PATCH 061/172] feat: add service class to create and save snapshot to database --- .../persistence/PersistenceMapper.java | 2 + .../persistence/PersistenceService.java | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 38b0e83d..fce315b3 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -10,11 +10,13 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import java.util.ArrayList; import java.util.List; +import org.springframework.stereotype.Component; /** * Helper class that is called by the PersistenceService to map all the tables when a snapshot is * created. */ +@Component public class PersistenceMapper { // Should be possible to just call save on Lobby object due to cascade. Will find out in the // future when this feature/ ticket is finished. diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java new file mode 100644 index 00000000..afa7f218 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -0,0 +1,42 @@ +package com.codenames.codenames.backend.database.persistence; + +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.repository.LobbyRepository; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** Service class for storing a snapshot into the database. */ +@Service +@Slf4j +@RequiredArgsConstructor +public class PersistenceService { + private final LobbyService lobbyService; + private final GameService gameService; + private final PersistenceMapper persistenceMapper; + private final LobbyRepository lobbyRepository; + + // according to spring docs I might need to configure it somewhere, will check later + // default setting is that it triggers rollback on error, should act as transaction from DBMS + + /** + * Method to create a snapshot and save it to the database. + * + * @param lobbyCode the code to identify a lobby + */ + @Transactional + public void saveSnapShot(String lobbyCode) { + log.info("Saving snapshot of lobby: {}", lobbyCode); + GameManager gameManager = gameService.getGameState(lobbyCode); + List playerList = lobbyService.getPlayersDto(lobbyCode); + LobbyEntity lobbySnapShot = + persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerList); + lobbyRepository.save(lobbySnapShot); + } +} From 83e310fafaae021a7a9a835d16455a449944fd47 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 15:28:08 +0200 Subject: [PATCH 062/172] fix: change method name to match entity class name --- .../codenames/backend/database/repository/PlayerRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 0d1b2766..6bb4a82c 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -20,5 +20,5 @@ public interface PlayerRepository extends JpaRepository { */ // First Lobby is the Lobby object inside the entity where we can find the lobby_code String. // And "resets" the SQL search back to the root table Player - Optional findByLobbyLobbyCodeAndUsername(String lobbyCode, String username); + Optional findByLobbyEntityLobbyCodeAndUsername(String lobbyCode, String username); } From a40c846dbf2032f5ca3ad0cc5c3e67b8470c126e Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 15:28:33 +0200 Subject: [PATCH 063/172] test: add test for player mapping --- .../persistence/PersistenceMappingTest.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java new file mode 100644 index 00000000..60e81a09 --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -0,0 +1,65 @@ +package com.codenames.codenames.backend.database.persistence; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.codenames.codenames.backend.database.entity.CardEntity; +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.entity.PlayerEntity; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class PersistenceMappingTest { + private PersistenceMapper persistenceMapper; + private String lobbyCode; + CardGenerator cardGenerator; + ClueValidationService clueValidationService; + GameManager gameManager; + PlayerDto player1; + PlayerDto player2; + List playerDtoList; + List dummyCardList; + + @BeforeEach + void setup() { + persistenceMapper = new PersistenceMapper(); + lobbyCode = "ABCDE"; + + cardGenerator = mock(CardGenerator.class); + + clueValidationService = mock(ClueValidationService.class); + gameManager = new GameManager(Team.RED, cardGenerator, clueValidationService); + player1 = new PlayerDto("Test1", Team.RED, Role.SPYMASTER, true); + player2 = new PlayerDto("Test2", Team.RED, Role.OPERATIVE, false); + playerDtoList = List.of(player1, player2); + } + + @Test + void testMapPlayer() { + LobbyEntity lobbyEntity = + persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerDtoList); + List playerEntities = lobbyEntity.getPlayerEntities(); + + PlayerEntity player1 = playerEntities.get(0); + + assertEquals(lobbyEntity, player1.getLobbyEntity()); + assertEquals("Test1", player1.getUsername()); + assertTrue(player1.getIsHost()); + assertEquals("RED", player1.getTeam()); + assertEquals("SPYMASTER", player1.getRole()); + } + + +} From bc9e75e2596a66d992041cbddf2bf952c6033e32 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 15:58:34 +0200 Subject: [PATCH 064/172] refactor: move lobbyEntity creation to setUp --- .../database/persistence/PersistenceMappingTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 60e81a09..d82e9a64 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -1,18 +1,19 @@ package com.codenames.codenames.backend.database.persistence; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.codenames.codenames.backend.database.entity.CardEntity; +import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; @@ -30,7 +31,7 @@ class PersistenceMappingTest { PlayerDto player1; PlayerDto player2; List playerDtoList; - List dummyCardList; + LobbyEntity lobbyEntity; @BeforeEach void setup() { @@ -44,12 +45,13 @@ void setup() { player1 = new PlayerDto("Test1", Team.RED, Role.SPYMASTER, true); player2 = new PlayerDto("Test2", Team.RED, Role.OPERATIVE, false); playerDtoList = List.of(player1, player2); + + lobbyEntity = + persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerDtoList); } @Test void testMapPlayer() { - LobbyEntity lobbyEntity = - persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerDtoList); List playerEntities = lobbyEntity.getPlayerEntities(); PlayerEntity player1 = playerEntities.get(0); From e297caa6eb7a8d5860a46f2dd53c10505dc3b3e8 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 16:05:50 +0200 Subject: [PATCH 065/172] test: add test for game state mapping --- .../database/persistence/PersistenceMappingTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index d82e9a64..ea6a3577 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -63,5 +63,15 @@ void testMapPlayer() { assertEquals("SPYMASTER", player1.getRole()); } - + @Test + void testMapGameState() { + GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); + assertEquals(lobbyEntity, gameStateEntity.getLobbyEntity()); + assertEquals(lobbyCode, gameStateEntity.getLobbyCode()); + assertEquals("RED", gameStateEntity.getCurrentTurn()); + assertEquals("SPYMASTER", gameStateEntity.getCurrentPhase()); + assertNull(gameStateEntity.getClueWord()); + assertEquals(0, gameStateEntity.getClueGuessAmount()); + assertEquals(0, gameStateEntity.getRemainingGuesses()); + } } From 6369afd7a4aef631a591fb132558c09f555cec27 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 16:32:15 +0200 Subject: [PATCH 066/172] test: add test for card mapping --- .../persistence/PersistenceMappingTest.java | 60 +++++++++++++++---- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index ea6a3577..80c7dd91 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -1,6 +1,7 @@ package com.codenames.codenames.backend.database.persistence; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -14,10 +15,12 @@ import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -25,8 +28,8 @@ class PersistenceMappingTest { private PersistenceMapper persistenceMapper; private String lobbyCode; - CardGenerator cardGenerator; - ClueValidationService clueValidationService; + CardGenerator mockCardGenerator; + ClueValidationService mockClueValidationService; GameManager gameManager; PlayerDto player1; PlayerDto player2; @@ -38,10 +41,10 @@ void setup() { persistenceMapper = new PersistenceMapper(); lobbyCode = "ABCDE"; - cardGenerator = mock(CardGenerator.class); + mockCardGenerator = mock(CardGenerator.class); - clueValidationService = mock(ClueValidationService.class); - gameManager = new GameManager(Team.RED, cardGenerator, clueValidationService); + mockClueValidationService = mock(ClueValidationService.class); + gameManager = new GameManager(Team.RED, mockCardGenerator, mockClueValidationService); player1 = new PlayerDto("Test1", Team.RED, Role.SPYMASTER, true); player2 = new PlayerDto("Test2", Team.RED, Role.OPERATIVE, false); playerDtoList = List.of(player1, player2); @@ -50,17 +53,34 @@ void setup() { persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerDtoList); } + // Modified helper methods from GameManager to generate a game with full cards + private void mockCardGeneration(List cardList) { + when(mockCardGenerator.generateCards(anyInt(), anyInt(), anyInt(), anyInt(), anyInt())) + .thenReturn(cardList); + } + + private GameManager helperMethodGenerateFullCardList(Color cardColor, Team startingTeam) { + List cardList = new ArrayList<>(); + for (int i = 0; i < 25; i++) { + cardList.add(new Card("Test" + i, cardColor)); + } + mockCardGeneration(cardList); + GameManager fullListGameManager = + new GameManager(startingTeam, mockCardGenerator, mockClueValidationService); + return fullListGameManager; + } + @Test void testMapPlayer() { List playerEntities = lobbyEntity.getPlayerEntities(); - PlayerEntity player1 = playerEntities.get(0); + PlayerEntity retrievedPlayer1 = playerEntities.get(0); - assertEquals(lobbyEntity, player1.getLobbyEntity()); - assertEquals("Test1", player1.getUsername()); - assertTrue(player1.getIsHost()); - assertEquals("RED", player1.getTeam()); - assertEquals("SPYMASTER", player1.getRole()); + assertEquals(lobbyEntity, retrievedPlayer1.getLobbyEntity()); + assertEquals("Test1", retrievedPlayer1.getUsername()); + assertTrue(retrievedPlayer1.getIsHost()); + assertEquals("RED", retrievedPlayer1.getTeam()); + assertEquals("SPYMASTER", retrievedPlayer1.getRole()); } @Test @@ -74,4 +94,22 @@ void testMapGameState() { assertEquals(0, gameStateEntity.getClueGuessAmount()); assertEquals(0, gameStateEntity.getRemainingGuesses()); } + + @Test + void testMapCard() { + GameManager fullCardGameManager = helperMethodGenerateFullCardList(Color.RED, Team.RED); + LobbyEntity fullCardLobbyEntity = + persistenceMapper.mapAggregateParentLobbyEntity( + lobbyCode, fullCardGameManager, playerDtoList); + List cardEntities = fullCardLobbyEntity.getCardEntities(); + + for (int i = 0; i < 25; i++) { + CardEntity cardEntity = cardEntities.get(i); + assertEquals(fullCardLobbyEntity, cardEntity.getLobbyEntity()); + assertEquals(i, cardEntity.getPosition()); + assertEquals("Test" + i, cardEntity.getWord()); + assertEquals("RED", cardEntity.getColor()); + assertFalse(cardEntity.getIsGuessed()); + } + } } From a8121924448655b79b5e21f460cacd5cbb6c1a14 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 16:36:09 +0200 Subject: [PATCH 067/172] refactor: change name for future maintainability --- .../persistence/PersistenceMappingTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 80c7dd91..eac6e5b8 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -35,6 +35,9 @@ class PersistenceMappingTest { PlayerDto player2; List playerDtoList; LobbyEntity lobbyEntity; + Color redColor = Color.RED; + Team redTeam = Team.RED; + int maxCardAmount = 25; @BeforeEach void setup() { @@ -61,13 +64,11 @@ private void mockCardGeneration(List cardList) { private GameManager helperMethodGenerateFullCardList(Color cardColor, Team startingTeam) { List cardList = new ArrayList<>(); - for (int i = 0; i < 25; i++) { + for (int i = 0; i < maxCardAmount; i++) { cardList.add(new Card("Test" + i, cardColor)); } mockCardGeneration(cardList); - GameManager fullListGameManager = - new GameManager(startingTeam, mockCardGenerator, mockClueValidationService); - return fullListGameManager; + return new GameManager(startingTeam, mockCardGenerator, mockClueValidationService); } @Test @@ -97,13 +98,13 @@ void testMapGameState() { @Test void testMapCard() { - GameManager fullCardGameManager = helperMethodGenerateFullCardList(Color.RED, Team.RED); + GameManager fullCardGameManager = helperMethodGenerateFullCardList(redColor, redTeam); LobbyEntity fullCardLobbyEntity = persistenceMapper.mapAggregateParentLobbyEntity( lobbyCode, fullCardGameManager, playerDtoList); List cardEntities = fullCardLobbyEntity.getCardEntities(); - for (int i = 0; i < 25; i++) { + for (int i = 0; i < maxCardAmount; i++) { CardEntity cardEntity = cardEntities.get(i); assertEquals(fullCardLobbyEntity, cardEntity.getLobbyEntity()); assertEquals(i, cardEntity.getPosition()); From 52da7efe9f53adf31b84ea54a00816b9af1a4c03 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 17:18:20 +0200 Subject: [PATCH 068/172] refactor: remove unused imports --- .../backend/database/persistence/PersistenceMapper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index fce315b3..7ae47668 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -38,14 +38,14 @@ public LobbyEntity mapAggregateParentLobbyEntity( List cardList = gameManager.getCardList(); lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); - lobbyEntity.setCardEntities(mapCard(lobbyEntity, lobbyCode, cardList)); - lobbyEntity.setPlayerEntities(mapPlayer(lobbyEntity, lobbyCode, players)); + lobbyEntity.setCardEntities(mapCard(lobbyEntity, cardList)); + lobbyEntity.setPlayerEntities(mapPlayer(lobbyEntity, players)); return lobbyEntity; } private List mapPlayer( - LobbyEntity lobbyEntity, String lobbyCode, List players) { + LobbyEntity lobbyEntity, List players) { List playerList = new ArrayList<>(); for (int i = 0; i < players.size(); i++) { @@ -76,7 +76,7 @@ private GameStateEntity mapGameState( return gameStateEntity; } - private List mapCard(LobbyEntity lobbyEntity, String lobbyCode, List cards) { + private List mapCard(LobbyEntity lobbyEntity, List cards) { List cardList = new ArrayList<>(); for (int i = 0; i < cards.size(); i++) { CardEntity cardEntity = new CardEntity(); From 2536fb426e62f637e0f35a0b95fe5f6a53aa234a Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 17:42:44 +0200 Subject: [PATCH 069/172] refactor: remove lombok constructor annotation Remove annotation and use a normal constructor to reduce mental overhead when using the service class. With constructor and javadoc referencing how to build constructor becomes easier. --- .../persistence/PersistenceService.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java index afa7f218..3b9a8d8d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -7,7 +7,6 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; import java.util.List; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,13 +14,28 @@ /** Service class for storing a snapshot into the database. */ @Service @Slf4j -@RequiredArgsConstructor public class PersistenceService { private final LobbyService lobbyService; private final GameService gameService; private final PersistenceMapper persistenceMapper; private final LobbyRepository lobbyRepository; + /** + * Constructor for the PersistenceService class. + * + * @param lobbyService the service class for lobby, from which we derive playerDto + * @param gameService the service class for game, from which we derive gameManager + * @param persistenceMapper the helper class used for ORM (Object Relational Mapping) + * @param lobbyRepository the repository for saving to DB with cascade + */ + public PersistenceService(LobbyService lobbyService, GameService gameService, + PersistenceMapper persistenceMapper, LobbyRepository lobbyRepository) { + this.lobbyService = lobbyService; + this.gameService = gameService; + this.persistenceMapper = persistenceMapper; + this.lobbyRepository = lobbyRepository; + } + // according to spring docs I might need to configure it somewhere, will check later // default setting is that it triggers rollback on error, should act as transaction from DBMS From e58c5213b1fd2e2c84e60577da887188438ed3e6 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 19:06:10 +0200 Subject: [PATCH 070/172] fix: add null guard clause to avoid null pointer exception --- .../database/persistence/PersistenceMapper.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 7ae47668..01210d90 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -6,6 +6,7 @@ import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import java.util.ArrayList; @@ -71,8 +72,15 @@ private GameStateEntity mapGameState( gameStateEntity.setCurrentTurn(gameManager.getCurrentTurn().name()); gameStateEntity.setCurrentPhase(gameManager.getCurrentPhase().name()); gameStateEntity.setClueWord(gameManager.getCurrentClueWord()); - gameStateEntity.setClueGuessAmount(gameStateEntity.getClueGuessAmount()); - gameStateEntity.setRemainingGuesses(gameStateEntity.getRemainingGuesses()); + + Clue currentClue = gameManager.getCurrentClue(); + if (currentClue != null) { + gameStateEntity.setClueGuessAmount(gameManager.getCurrentClue().guessAmount()); + gameStateEntity.setRemainingGuesses(gameManager.getRemainingGuesses()); + } else { + gameStateEntity.setClueGuessAmount(0); + gameStateEntity.setRemainingGuesses(0); + } return gameStateEntity; } From ef25c845bda7d8044f6dbe982cd6cd4f94678c0f Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 20:52:28 +0200 Subject: [PATCH 071/172] test: add mocking and setUp() for integration test --- .../persistence/PersistenceServiceTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java new file mode 100644 index 00000000..32760b25 --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -0,0 +1,101 @@ +package com.codenames.codenames.backend.database.persistence; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.codenames.codenames.backend.database.repository.LobbyRepository; +import com.codenames.codenames.backend.game.application.CardGenerator; +import com.codenames.codenames.backend.game.application.ClueValidationService; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + + +// (Spring Docs) +// By default, tests annotated with @DataJpaTest are transactional and roll back at the end of each +// test. +// Also, only uses @Entity annotated classes +@DataJpaTest +class PersistenceServiceTest { + private final LobbyRepository lobbyRepository; + private PersistenceService persistenceService; + private LobbyService mockLobbyService; + private GameService mockGameService; + private CardGenerator mockCardGenerator; + private ClueValidationService mockClueValidationService; + private GameManager gameManager; + private List playerDtoList; + private PlayerDto player1; + private PlayerDto player2; + + private final String lobbyCode = "ABCDE"; + private final int maxCardAmount = 25; + private final Color redColor = Color.RED; + private final Team redTeam = Team.RED; + private final Role spymaster = Role.SPYMASTER; + private final Role operative = Role.OPERATIVE; + + // Cant use new to instantiate Interfaces, Spring will automatically use dependency injection here + public PersistenceServiceTest(LobbyRepository lobbyRepository) { + this.lobbyRepository = lobbyRepository; + } + + @BeforeEach + void setUp() { + mockLobbyService = mock(LobbyService.class); + mockGameService = mock(GameService.class); + PersistenceMapper persistenceMapper = new PersistenceMapper(); + + persistenceService = + new PersistenceService(mockLobbyService, mockGameService, persistenceMapper, lobbyRepository); + + mockCardGenerator = mock(CardGenerator.class); + mockClueValidationService = mock(ClueValidationService.class); + + gameManager = helperMethodGenerateFullCardList(redColor, redTeam); + + player1 = new PlayerDto("Test1", redTeam, spymaster, true); + player2 = new PlayerDto("Test2", redTeam, operative, false); + + playerDtoList = List.of(player1, player2); + + // controlling the saveSnapShot internal methods + when(mockGameService.getGameState(lobbyCode)).thenReturn(gameManager); + when(mockLobbyService.getPlayersDto(lobbyCode)).thenReturn(playerDtoList); + } + + private void mockCardGeneration(List cardList) { + when(mockCardGenerator.generateCards(anyInt(), anyInt(), anyInt(), anyInt(), anyInt())) + .thenReturn(cardList); + } + + private GameManager helperMethodGenerateFullCardList(Color cardColor, Team startingTeam) { + List cardList = new ArrayList<>(); + for (int i = 0; i < maxCardAmount; i++) { + cardList.add(new Card("Test" + i, cardColor)); + } + mockCardGeneration(cardList); + return new GameManager(startingTeam, mockCardGenerator, mockClueValidationService); + } + + + @Test + void testSaveSnapshot() { + persistenceService.saveSnapShot(lobbyCode); + + } + + + +} From 0120711542f180f9c0595efae57834b405d9f88a Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 20:53:11 +0200 Subject: [PATCH 072/172] refactor: use variables instead of directly calling enums --- .../persistence/PersistenceMappingTest.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index eac6e5b8..9085187a 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -28,15 +28,17 @@ class PersistenceMappingTest { private PersistenceMapper persistenceMapper; private String lobbyCode; - CardGenerator mockCardGenerator; - ClueValidationService mockClueValidationService; - GameManager gameManager; - PlayerDto player1; - PlayerDto player2; - List playerDtoList; - LobbyEntity lobbyEntity; - Color redColor = Color.RED; - Team redTeam = Team.RED; + private CardGenerator mockCardGenerator; + private ClueValidationService mockClueValidationService; + private GameManager gameManager; + private PlayerDto player1; + private PlayerDto player2; + private List playerDtoList; + private LobbyEntity lobbyEntity; + private final Color redColor = Color.RED; + private final Team redTeam = Team.RED; + private final Role spymaster = Role.SPYMASTER; + private final Role operative = Role.OPERATIVE; int maxCardAmount = 25; @BeforeEach @@ -47,9 +49,9 @@ void setup() { mockCardGenerator = mock(CardGenerator.class); mockClueValidationService = mock(ClueValidationService.class); - gameManager = new GameManager(Team.RED, mockCardGenerator, mockClueValidationService); - player1 = new PlayerDto("Test1", Team.RED, Role.SPYMASTER, true); - player2 = new PlayerDto("Test2", Team.RED, Role.OPERATIVE, false); + gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); + player1 = new PlayerDto("Test1", redTeam, spymaster, true); + player2 = new PlayerDto("Test2", redTeam, operative, false); playerDtoList = List.of(player1, player2); lobbyEntity = From 21e4596de774de68b78204cb4fe78ff273579c2f Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:07:44 +0200 Subject: [PATCH 073/172] test: add test for lobbyEntity --- .../database/persistence/PersistenceServiceTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 32760b25..ef608d3d 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -1,9 +1,14 @@ package com.codenames.codenames.backend.database.persistence; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.database.entity.GameStateEntity; +import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.database.repository.LobbyRepository; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.ClueValidationService; @@ -89,6 +94,13 @@ private GameManager helperMethodGenerateFullCardList(Color cardColor, Team start return new GameManager(startingTeam, mockCardGenerator, mockClueValidationService); } + @Test + void testSaveSnapshot_lobbyEntity() { + persistenceService.saveSnapShot(lobbyCode); + + assertNotNull(lobbyRepository.findById(lobbyCode)); + assertEquals(lobbyCode, lobbyRepository.findById(lobbyCode).get().getLobbyCode()); + } @Test void testSaveSnapshot() { From 31d4ba8442fa9995d80b44375ffcbca66f4f3907 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:08:04 +0200 Subject: [PATCH 074/172] test: add test for game state entity --- .../persistence/PersistenceServiceTest.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index ef608d3d..22c78ae6 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -103,11 +103,19 @@ void testSaveSnapshot_lobbyEntity() { } @Test - void testSaveSnapshot() { + void testSaveSnapshot_gameStateEntity() { persistenceService.saveSnapShot(lobbyCode); - } - + LobbyEntity lobbyEntity = lobbyRepository.findById(lobbyCode).get(); + GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); + assertNotNull(gameStateEntity); + assertEquals(lobbyCode, gameStateEntity.getLobbyCode()); + assertEquals("RED", gameStateEntity.getCurrentTurn()); + assertEquals("SPYMASTER", gameStateEntity.getCurrentPhase()); + assertNull(gameStateEntity.getClueWord()); + assertEquals(0, gameStateEntity.getClueGuessAmount()); + assertEquals(0, gameStateEntity.getRemainingGuesses()); + } } From 10fe4ef73248de3622b1d9ac49a5d429f2cb7dd3 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:25:27 +0200 Subject: [PATCH 075/172] test: add test for player entity --- .../persistence/PersistenceServiceTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 22c78ae6..9db5c587 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -3,12 +3,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.database.repository.LobbyRepository; import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.ClueValidationService; @@ -118,4 +120,21 @@ void testSaveSnapshot_gameStateEntity() { assertEquals(0, gameStateEntity.getRemainingGuesses()); } + @Test + void testSaveSnapshot_player() { + persistenceService.saveSnapShot(lobbyCode); + + LobbyEntity lobbyEntity = lobbyRepository.findById(lobbyCode).get(); + List playerList = lobbyEntity.getPlayerEntities(); + assertEquals(2, playerList.size()); + + PlayerEntity player1 = playerList.get(0); + + assertEquals(lobbyCode, player1.getLobbyEntity().getLobbyCode()); + assertEquals("Test1", player1.getUsername()); + assertTrue(player1.getIsHost()); + assertEquals("RED", player1.getTeam()); + assertEquals("SPYMASTER", player1.getRole()); + } + } From a13381e0985a0f3c7feba04054969b36e1129d91 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:30:37 +0200 Subject: [PATCH 076/172] test: add test for card entity test --- .../persistence/PersistenceServiceTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 9db5c587..da9e0cf5 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -1,6 +1,7 @@ package com.codenames.codenames.backend.database.persistence; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -8,6 +9,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.database.entity.CardEntity; import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.database.entity.PlayerEntity; @@ -137,4 +139,29 @@ void testSaveSnapshot_player() { assertEquals("SPYMASTER", player1.getRole()); } + @Test + void testSaveSnapshot_card() { + persistenceService.saveSnapShot(lobbyCode); + + LobbyEntity lobbyEntity = lobbyRepository.findById(lobbyCode).get(); + List cardList = lobbyEntity.getCardEntities(); + + assertEquals(25, cardList.size()); + assertEquals(lobbyCode, cardList.getFirst().getLobbyEntity().getLobbyCode()); + + assertEquals(0, cardList.getFirst().getPosition()); + assertEquals("Test1", cardList.getFirst().getWord()); + assertEquals("RED", cardList.getFirst().getColor()); + assertFalse(cardList.getFirst().getIsGuessed()); + + assertEquals(24, cardList.getLast().getPosition()); + assertEquals("Test24", cardList.getLast().getWord()); + assertEquals("RED", cardList.getFirst().getColor()); + assertFalse(cardList.getLast().getIsGuessed()); + + + + + } + } From 70843770768ee78124ab3fb1c064ed39ed37fccf Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:33:50 +0200 Subject: [PATCH 077/172] fix: repalce getFirst() with get(x) getFirst() results in compilation error, cannot find symbol error --- .../persistence/PersistenceServiceTest.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index da9e0cf5..cdceb0a4 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -147,21 +147,16 @@ void testSaveSnapshot_card() { List cardList = lobbyEntity.getCardEntities(); assertEquals(25, cardList.size()); - assertEquals(lobbyCode, cardList.getFirst().getLobbyEntity().getLobbyCode()); - - assertEquals(0, cardList.getFirst().getPosition()); - assertEquals("Test1", cardList.getFirst().getWord()); - assertEquals("RED", cardList.getFirst().getColor()); - assertFalse(cardList.getFirst().getIsGuessed()); - - assertEquals(24, cardList.getLast().getPosition()); - assertEquals("Test24", cardList.getLast().getWord()); - assertEquals("RED", cardList.getFirst().getColor()); - assertFalse(cardList.getLast().getIsGuessed()); - - + assertEquals(lobbyCode, cardList.get(0).getLobbyEntity().getLobbyCode()); + assertEquals(0, cardList.get(0).getPosition()); + assertEquals("Test1", cardList.get(0).getWord()); + assertEquals("RED", cardList.get(0).getColor()); + assertFalse(cardList.get(0).getIsGuessed()); + assertEquals(24, cardList.get(24).getPosition()); + assertEquals("Test24", cardList.get(24).getWord()); + assertEquals("RED", cardList.get(24).getColor()); + assertFalse(cardList.get(24).getIsGuessed()); } - } From 79b0906927bb8acde242fb92ad7ee13ceb771a73 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:42:24 +0200 Subject: [PATCH 078/172] fix: add CreationTimestamp annotation to generate the timestamp for DB Source Baeldung website (Forgot when creating class and threw null constraint violation when testing) --- .../codenames/backend/database/entity/LobbyEntity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java b/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java index 0b940a8b..24d1b59c 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/LobbyEntity.java @@ -13,6 +13,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.SourceType; /** * Persistent entity representing a lobby record within the database schema. @@ -27,6 +29,7 @@ public class LobbyEntity { @Column(name = "lobby_code", length = 5, nullable = false) private String lobbyCode; @Column(name = "created_at", insertable = false, updatable = false, nullable = false) + @CreationTimestamp(source = SourceType.DB) private Timestamp createdAt; @OneToOne(mappedBy = "lobbyEntity", cascade = CascadeType.ALL, orphanRemoval = true) From 6c32e2dab2cef1a51621b054c206d8f889416dd9 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:51:37 +0200 Subject: [PATCH 079/172] fix: add autowired annotation to allow repository to be initialized --- .../backend/database/persistence/PersistenceServiceTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index cdceb0a4..1902e5a8 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -28,6 +28,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -55,7 +56,9 @@ class PersistenceServiceTest { private final Role spymaster = Role.SPYMASTER; private final Role operative = Role.OPERATIVE; - // Cant use new to instantiate Interfaces, Spring will automatically use dependency injection here + // Cant use new to instantiate Interfaces --> use @Autowired + // Spring checks their active beans (@Repository in this case) and upon matching injects it here + @Autowired public PersistenceServiceTest(LobbyRepository lobbyRepository) { this.lobbyRepository = lobbyRepository; } From bb98bfcd94b82da6039cff0dfbed1cfe160d3d6a Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:52:12 +0200 Subject: [PATCH 080/172] refactor: rename playerEntity to avoid name shadowing --- .../database/persistence/PersistenceServiceTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 1902e5a8..9d5532bc 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -133,13 +133,13 @@ void testSaveSnapshot_player() { List playerList = lobbyEntity.getPlayerEntities(); assertEquals(2, playerList.size()); - PlayerEntity player1 = playerList.get(0); + PlayerEntity playerEntity = playerList.get(0); - assertEquals(lobbyCode, player1.getLobbyEntity().getLobbyCode()); - assertEquals("Test1", player1.getUsername()); - assertTrue(player1.getIsHost()); - assertEquals("RED", player1.getTeam()); - assertEquals("SPYMASTER", player1.getRole()); + assertEquals(lobbyCode, playerEntity.getLobbyEntity().getLobbyCode()); + assertEquals("Test1", playerEntity.getUsername()); + assertTrue(playerEntity.getIsHost()); + assertEquals("RED", playerEntity.getTeam()); + assertEquals("SPYMASTER", playerEntity.getRole()); } @Test From 9c8a40026282d929e2e537845302edfff6e17a01 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:52:47 +0200 Subject: [PATCH 081/172] style: lint code for google code style --- .../database/persistence/PersistenceServiceTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 9d5532bc..fe59a28c 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -31,7 +31,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; - // (Spring Docs) // By default, tests annotated with @DataJpaTest are transactional and roll back at the end of each // test. @@ -70,7 +69,8 @@ void setUp() { PersistenceMapper persistenceMapper = new PersistenceMapper(); persistenceService = - new PersistenceService(mockLobbyService, mockGameService, persistenceMapper, lobbyRepository); + new PersistenceService( + mockLobbyService, mockGameService, persistenceMapper, lobbyRepository); mockCardGenerator = mock(CardGenerator.class); mockClueValidationService = mock(ClueValidationService.class); @@ -153,13 +153,13 @@ void testSaveSnapshot_card() { assertEquals(lobbyCode, cardList.get(0).getLobbyEntity().getLobbyCode()); assertEquals(0, cardList.get(0).getPosition()); - assertEquals("Test1", cardList.get(0).getWord()); - assertEquals("RED", cardList.get(0).getColor()); + assertEquals("Test0", cardList.get(0).getWord()); + assertEquals("RED", cardList.get(0).getColor()); assertFalse(cardList.get(0).getIsGuessed()); assertEquals(24, cardList.get(24).getPosition()); assertEquals("Test24", cardList.get(24).getWord()); - assertEquals("RED", cardList.get(24).getColor()); + assertEquals("RED", cardList.get(24).getColor()); assertFalse(cardList.get(24).getIsGuessed()); } } From 952a937adaa41f5f03cc2cb727c0333a1a96dd42 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 21:59:54 +0200 Subject: [PATCH 082/172] refactor: extract duplicated method calls into variable --- .../backend/database/persistence/PersistenceServiceTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index fe59a28c..c91ae761 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -105,8 +105,9 @@ private GameManager helperMethodGenerateFullCardList(Color cardColor, Team start void testSaveSnapshot_lobbyEntity() { persistenceService.saveSnapShot(lobbyCode); - assertNotNull(lobbyRepository.findById(lobbyCode)); - assertEquals(lobbyCode, lobbyRepository.findById(lobbyCode).get().getLobbyCode()); + LobbyEntity lobbyEntity = lobbyRepository.findById(lobbyCode).get(); + assertNotNull(lobbyEntity); + assertEquals(lobbyCode, lobbyEntity.getLobbyCode()); } @Test From 8d75b7a3005bd271f6717e19dcd87ad0ce699264 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:22:34 +0200 Subject: [PATCH 083/172] test: add test for non Null clues --- .../persistence/PersistenceMappingTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 9085187a..eea415c9 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -15,6 +16,7 @@ import com.codenames.codenames.backend.game.application.CardGenerator; import com.codenames.codenames.backend.game.application.ClueValidationService; import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; @@ -98,6 +100,20 @@ void testMapGameState() { assertEquals(0, gameStateEntity.getRemainingGuesses()); } + @Test + void testMapGameState_notNullClue() { + GameManager gameManagerWithClue = helperMethodGenerateFullCardList(redColor, redTeam); + when(mockClueValidationService.validateWord(any(), any())).thenReturn(true); + gameManager.submitClue(new Clue("TestWord", 1), redTeam); + lobbyEntity = + persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerDtoList); + + GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); + assertEquals("TestWord", gameStateEntity.getClueWord()); + assertEquals(2, gameStateEntity.getClueGuessAmount()); + assertEquals(2, gameStateEntity.getRemainingGuesses()); + } + @Test void testMapCard() { GameManager fullCardGameManager = helperMethodGenerateFullCardList(redColor, redTeam); From 3e069d316fba1546e25581645d36508ee07290b7 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:27:26 +0200 Subject: [PATCH 084/172] chore: remove unused gameManager in test Forgot to use the new custom GameManager, turns out it was not needed? (Tests were failing due to invalid word so custom GameManager was added.) --- .../backend/database/persistence/PersistenceMappingTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index eea415c9..3468f368 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -102,7 +102,6 @@ void testMapGameState() { @Test void testMapGameState_notNullClue() { - GameManager gameManagerWithClue = helperMethodGenerateFullCardList(redColor, redTeam); when(mockClueValidationService.validateWord(any(), any())).thenReturn(true); gameManager.submitClue(new Clue("TestWord", 1), redTeam); lobbyEntity = From e173c467869bb790639d6cf4b72300d1a3c08519 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:33:51 +0200 Subject: [PATCH 085/172] chore: replace json persistence with DB persistence --- .../backend/game/api/GameSocketController.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 02eb5c0d..1c3aa35b 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -1,12 +1,12 @@ package com.codenames.codenames.backend.game.api; +import com.codenames.codenames.backend.database.persistence.PersistenceService; import com.codenames.codenames.backend.game.api.dto.ClueMessage; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @@ -23,7 +23,7 @@ public class GameSocketController { private final GameService gameService; private final SimpMessagingTemplate messagingTemplate; - private final SystemStatePersistenceService persistenceService; + private final PersistenceService persistenceService; private static final String GAME_TOPIC_PREFIX = "/topic/game/"; @@ -37,7 +37,7 @@ public class GameSocketController { public GameSocketController( GameService gameService, SimpMessagingTemplate messagingTemplate, - SystemStatePersistenceService persistenceService) { + PersistenceService persistenceService) { this.gameService = gameService; this.messagingTemplate = messagingTemplate; @@ -68,7 +68,7 @@ public void startGame(StartGameMessage message) { public void revealCard(RevealCardMessage message) { gameService.flipCard(message.getLobbyCode(), message.getPosition(), message.getCurrentTurn()); - persistenceService.persistCurrentState(); + persistenceService.saveSnapShot(message.getLobbyCode()); messagingTemplate.convertAndSend( GAME_TOPIC_PREFIX + message.getLobbyCode(), @@ -89,7 +89,7 @@ public void submitClue(ClueMessage message) { message.getLobbyCode(), new Clue(message.getWord(), message.getGuessAmount()), message.getCurrentTurn()); - persistenceService.persistCurrentState(); + persistenceService.saveSnapShot(message.getLobbyCode()); messagingTemplate.convertAndSend( GAME_TOPIC_PREFIX + message.getLobbyCode(), @@ -105,7 +105,7 @@ public void submitClue(ClueMessage message) { public void passTurn(PassTurnMessage message) { gameService.passTurn(message.getLobbyCode(), message.getCurrentTurn()); - persistenceService.persistCurrentState(); + persistenceService.saveSnapShot(message.getLobbyCode()); messagingTemplate.convertAndSend( GAME_TOPIC_PREFIX + message.getLobbyCode(), From 960c5db5adaeb5a5fe4c8dc84b40ec3bd1deea4f Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:34:08 +0200 Subject: [PATCH 086/172] fix: replace json persistence calls with DB persistence calls --- .../backend/game/api/GameSocketControllerTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 28519385..33e37e24 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -6,6 +6,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.database.persistence.PersistenceService; import com.codenames.codenames.backend.game.api.dto.ClueMessage; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; @@ -13,7 +14,6 @@ import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,7 +32,7 @@ class GameSocketControllerTest { @Mock private SimpMessagingTemplate messagingTemplate; - @Mock private SystemStatePersistenceService persistenceService; + @Mock private PersistenceService persistenceService; private GameSocketController controller; @@ -51,7 +51,7 @@ void startGameShouldBroadcastStateWithoutPersisting() { controller.startGame(message); - verify(persistenceService, never()).persistCurrentState(); + verify(persistenceService, never()).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } @@ -68,7 +68,7 @@ void revealCardShouldPersistAndBroadcastState() { controller.revealCard(message); verify(gameService).flipCard(LOBBY_CODE, 0, Team.RED); - verify(persistenceService).persistCurrentState(); + verify(persistenceService).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } @@ -86,7 +86,7 @@ void submitClueShouldPersistAndBroadcastState() { controller.submitClue(message); verify(gameService).submitClue(anyString(), any(), any()); - verify(persistenceService).persistCurrentState(); + verify(persistenceService).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } @@ -102,7 +102,7 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { controller.passTurn(message); verify(gameService).passTurn(LOBBY_CODE, Team.RED); - verify(persistenceService).persistCurrentState(); + verify(persistenceService).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } From 93a589dcf55cde9d35b1f00492424af66907f7d6 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:36:13 +0200 Subject: [PATCH 087/172] style: remove style violations (empty line, space) --- .../backend/database/persistence/PersistenceMapper.java | 1 - .../backend/database/persistence/PersistenceMappingTest.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 01210d90..d70978dd 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -5,7 +5,6 @@ import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.game.domain.Card; - import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 3468f368..907a08ea 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -39,8 +39,8 @@ class PersistenceMappingTest { private LobbyEntity lobbyEntity; private final Color redColor = Color.RED; private final Team redTeam = Team.RED; - private final Role spymaster = Role.SPYMASTER; - private final Role operative = Role.OPERATIVE; + private final Role spymaster = Role.SPYMASTER; + private final Role operative = Role.OPERATIVE; int maxCardAmount = 25; @BeforeEach From df41293a106e8b2ed4525bb0a21404eb7015c6e7 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 22:44:16 +0200 Subject: [PATCH 088/172] chore: remove comments Transactional should be auto configured since we are using SpringBoot if I understand correctly --- .../backend/database/persistence/PersistenceMapper.java | 3 --- .../backend/database/persistence/PersistenceService.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index d70978dd..c3d54a2b 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -18,9 +18,6 @@ */ @Component public class PersistenceMapper { - // Should be possible to just call save on Lobby object due to cascade. Will find out in the - // future when this feature/ ticket is finished. - /** * This method creates and returns a lobby entity that is to be stored into the DB. * diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java index 3b9a8d8d..bf218cb8 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -36,9 +36,6 @@ public PersistenceService(LobbyService lobbyService, GameService gameService, this.lobbyRepository = lobbyRepository; } - // according to spring docs I might need to configure it somewhere, will check later - // default setting is that it triggers rollback on error, should act as transaction from DBMS - /** * Method to create a snapshot and save it to the database. * From c846616665b11147a2046316894373e2ba35ae91 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sat, 6 Jun 2026 23:03:13 +0200 Subject: [PATCH 089/172] fix: remove persistence after flipping card Saving to DB is only meant to happen after a turn is over. --- .../codenames/backend/game/api/GameSocketController.java | 1 - .../codenames/backend/game/api/GameSocketControllerTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 1c3aa35b..7d00a4c6 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -68,7 +68,6 @@ public void startGame(StartGameMessage message) { public void revealCard(RevealCardMessage message) { gameService.flipCard(message.getLobbyCode(), message.getPosition(), message.getCurrentTurn()); - persistenceService.saveSnapShot(message.getLobbyCode()); messagingTemplate.convertAndSend( GAME_TOPIC_PREFIX + message.getLobbyCode(), diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 33e37e24..ea9e8e13 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -68,7 +68,6 @@ void revealCardShouldPersistAndBroadcastState() { controller.revealCard(message); verify(gameService).flipCard(LOBBY_CODE, 0, Team.RED); - verify(persistenceService).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } From 8d979668b58949e093935f2d4c4ec8109a0d75e2 Mon Sep 17 00:00:00 2001 From: XtophB Date: Mon, 8 Jun 2026 23:45:47 +0200 Subject: [PATCH 090/172] feat: create mapping method for GameManager restoration --- .../restoration/RestorationMapper.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java new file mode 100644 index 00000000..9f8e19cf --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -0,0 +1,48 @@ +package com.codenames.codenames.backend.database.restoration; + +import com.codenames.codenames.backend.database.entity.GameStateEntity; +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.ArrayList; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class RestorationMapper { + + public Lobby mapToLobby(LobbyEntity lobbyEntity) { + Lobby lobby = new Lobby("sd", "d"); + GameStateDto gameStateDto = mapToGameStateDto(lobbyEntity); + return lobby; + } + + private GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { + GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); + if (gameStateEntity == null) { + throw new RuntimeException("Game state entity is null"); + } + + Team winner = null; + // valueOf takes string and returns enum of that string + Team currentTeam = Team.valueOf(gameStateEntity.getCurrentTurn()); + Role currentPhase = Role.valueOf(gameStateEntity.getCurrentPhase()); + ClueDto clueDto = null; + if(gameStateEntity.getClueWord() != null) { + clueDto = new ClueDto(gameStateEntity.getClueWord(), gameStateEntity.getClueGuessAmount()); + } + List cardList = new ArrayList<>(25); + + return new GameStateDto(winner, currentTeam, currentPhase, clueDto, cardList); + + } + + private List mapToCardDto(LobbyEntity lobbyEntity) { + + } +} From 4e283df2500f04338ab24cb7524f5781078079ff Mon Sep 17 00:00:00 2001 From: XtophB Date: Mon, 8 Jun 2026 23:51:45 +0200 Subject: [PATCH 091/172] feat: add mapping for CardList for mapping to GameState --- .../restoration/RestorationMapper.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 9f8e19cf..4b307e1b 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -1,11 +1,13 @@ package com.codenames.codenames.backend.database.restoration; +import com.codenames.codenames.backend.database.entity.CardEntity; import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -33,16 +35,26 @@ private GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { Team currentTeam = Team.valueOf(gameStateEntity.getCurrentTurn()); Role currentPhase = Role.valueOf(gameStateEntity.getCurrentPhase()); ClueDto clueDto = null; - if(gameStateEntity.getClueWord() != null) { + if (gameStateEntity.getClueWord() != null) { clueDto = new ClueDto(gameStateEntity.getClueWord(), gameStateEntity.getClueGuessAmount()); } - List cardList = new ArrayList<>(25); - + List cardList = mapToCardDto(lobbyEntity); + mapToCardDto(lobbyEntity); return new GameStateDto(winner, currentTeam, currentPhase, clueDto, cardList); - } private List mapToCardDto(LobbyEntity lobbyEntity) { + List cardList = new ArrayList<>(25); + for (int i = 0; i < 25; i++) { + CardEntity currentCardEntity = lobbyEntity.getCardEntities().get(i); + CardDto cardDto = + new CardDto( + currentCardEntity.getWord(), + Color.valueOf(currentCardEntity.getColor()), + currentCardEntity.getIsGuessed()); + cardList.add(cardDto); + } + return cardList; } } From cd7760ae1e7fff8943f6f8ecae29f5572fd9ac93 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 00:00:57 +0200 Subject: [PATCH 092/172] feat: add mapping to PlayerDto --- .../restoration/RestorationMapper.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 4b307e1b..24438ece 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -3,11 +3,13 @@ import com.codenames.codenames.backend.database.entity.CardEntity; import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.entity.PlayerEntity; import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; @@ -24,6 +26,8 @@ public Lobby mapToLobby(LobbyEntity lobbyEntity) { return lobby; } + // TODO: I need to create a HashMap with lobbyCode:GameStateDto for this method i want to copy + // TODO: from JSON Recovery ```gameService.restoreGameManager(lobbyCode, gameStateManager);``` private GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); if (gameStateEntity == null) { @@ -57,4 +61,20 @@ private List mapToCardDto(LobbyEntity lobbyEntity) { } return cardList; } + + private List mapToPlayerDto(LobbyEntity lobbyEntity) { + List playerList = new ArrayList<>(); + + for (int i = 0; i < lobbyEntity.getPlayerEntities().size(); i++) { + PlayerEntity playerEntity = lobbyEntity.getPlayerEntities().get(i); + PlayerDto playerDto = + new PlayerDto( + playerEntity.getUsername(), + Team.valueOf(playerEntity.getTeam()), + Role.valueOf(playerEntity.getRole()), + playerEntity.getIsHost()); + playerList.add(playerDto); + } + return playerList; + } } From cde42eaa34b5aa5bd89fe933c223f836d78c9a18 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 00:16:58 +0200 Subject: [PATCH 093/172] feat: add helper methods to re-build lobby --- .../restoration/RestorationMapper.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 24438ece..a969e4f8 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -7,7 +7,6 @@ import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Card; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; @@ -21,7 +20,7 @@ public class RestorationMapper { public Lobby mapToLobby(LobbyEntity lobbyEntity) { - Lobby lobby = new Lobby("sd", "d"); + Lobby lobby = new Lobby("", ""); GameStateDto gameStateDto = mapToGameStateDto(lobbyEntity); return lobby; } @@ -77,4 +76,34 @@ private List mapToPlayerDto(LobbyEntity lobbyEntity) { } return playerList; } + + private Lobby buildLobby(String lobbyCode, List players) { + String hostUsername = findHostUsername(players); + + Lobby lobby = new Lobby(lobbyCode, hostUsername); + + for (PlayerDto player : players) { + if (!player.username().equals(hostUsername)) { + lobby.addPlayer(player.username(), player.isHost()); + } + if (player.team() != null) { + lobby.setPlayerTeam(player.username(), player.team()); + } + if (player.role() != null) { + lobby.setPlayerRole(player.username(), player.role()); + } + } + + return lobby; + } + + private static String findHostUsername(List players) { + String hostUsername = ""; + for (PlayerDto playerDto : players) { + if (playerDto.isHost()){ + hostUsername = playerDto.username(); + } + } + return hostUsername; + } } From 7a3fad3a395624a1cea774855eb92216eac42fe8 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 00:39:20 +0200 Subject: [PATCH 094/172] feat: add lobby mapper --- .../backend/database/restoration/RestorationMapper.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index a969e4f8..1a98eb0f 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -20,13 +20,12 @@ public class RestorationMapper { public Lobby mapToLobby(LobbyEntity lobbyEntity) { - Lobby lobby = new Lobby("", ""); - GameStateDto gameStateDto = mapToGameStateDto(lobbyEntity); + String lobbyCode = lobbyEntity.getLobbyCode(); + List playerDtoList = mapToPlayerDto(lobbyEntity); + Lobby lobby = buildLobby(lobbyCode, playerDtoList); return lobby; } - // TODO: I need to create a HashMap with lobbyCode:GameStateDto for this method i want to copy - // TODO: from JSON Recovery ```gameService.restoreGameManager(lobbyCode, gameStateManager);``` private GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); if (gameStateEntity == null) { From ce7558c6cea4107d15acaaac1aca02df41aceccb Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 00:52:40 +0200 Subject: [PATCH 095/172] fix: expose method to allow Service to use it --- .../backend/database/restoration/RestorationMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 1a98eb0f..cfdaf880 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -8,6 +8,7 @@ import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; +import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Role; @@ -26,7 +27,7 @@ public Lobby mapToLobby(LobbyEntity lobbyEntity) { return lobby; } - private GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { + public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); if (gameStateEntity == null) { throw new RuntimeException("Game state entity is null"); From 81ab52f7636a4c105f715f7da91a9b68390860a2 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 00:53:17 +0200 Subject: [PATCH 096/172] feat: add service class to handle existing restoration methods --- .../restoration/RestorationService.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java new file mode 100644 index 00000000..880d188c --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java @@ -0,0 +1,45 @@ +package com.codenames.codenames.backend.database.restoration; + +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.repository.LobbyRepository; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import jakarta.annotation.PostConstruct; +import org.springframework.stereotype.Service; + +@Service +public class RestorationService { + private final RestorationMapper restorationMapper; + private final LobbyRepository lobbyRepository; + + private final LobbyService lobbyService; + private final GameService gameService; + private final GameManagerFactory gameManagerFactory; + + public RestorationService(RestorationMapper restorationMapper, LobbyRepository lobbyRepository, + LobbyService lobbyService, GameService gameService, GameManagerFactory gameManagerFactory) { + this.restorationMapper = restorationMapper; + this.lobbyRepository = lobbyRepository; + this.lobbyService = lobbyService; + this.gameService = gameService; + this.gameManagerFactory = gameManagerFactory; + } + + @PostConstruct + public void restoreOnContainerStart() { + for (LobbyEntity lobbyEntity : lobbyRepository.findAll()) { + String lobbyCode = lobbyEntity.getLobbyCode(); + + Lobby lobbyObj = restorationMapper.mapToLobby(lobbyEntity); + lobbyService.restoreLobby(lobbyCode, lobbyObj); + + GameStateDto gameStateDtoObj = restorationMapper.mapToGameStateDto(lobbyEntity); + GameManager gameManagerObj = gameManagerFactory.createFromSnapshot(gameStateDtoObj); + gameService.restoreGameManager(lobbyCode, gameManagerObj); + } + } +} From a419673666d5dafde42ea108fb0e41d0ec97ad55 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 01:03:33 +0200 Subject: [PATCH 097/172] fix: remove erroneous leftover code --- .../backend/database/restoration/RestorationMapper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index cfdaf880..a6b95e40 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -42,7 +42,6 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { clueDto = new ClueDto(gameStateEntity.getClueWord(), gameStateEntity.getClueGuessAmount()); } List cardList = mapToCardDto(lobbyEntity); - mapToCardDto(lobbyEntity); return new GameStateDto(winner, currentTeam, currentPhase, clueDto, cardList); } From 6b9d60af874e098404ddb5e4bd5e91251d523c64 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 01:04:33 +0200 Subject: [PATCH 098/172] feat: add transactional annotation to ensure atomicity --- .../backend/database/restoration/RestorationService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java index 880d188c..e546663d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java @@ -9,9 +9,11 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Lobby; import jakarta.annotation.PostConstruct; +import org.springframework.transaction.annotation.Transactional; import org.springframework.stereotype.Service; @Service +@Transactional(readOnly = true) public class RestorationService { private final RestorationMapper restorationMapper; private final LobbyRepository lobbyRepository; From 8abca8bd122444d76773c2d24958f01bda11b316 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 01:18:03 +0200 Subject: [PATCH 099/172] style: add javaDocs --- .../restoration/RestorationMapper.java | 23 +++++++++++++++---- .../restoration/RestorationService.java | 18 ++++++++++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index a6b95e40..470175cf 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -8,7 +8,6 @@ import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.domain.Color; -import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; import com.codenames.codenames.backend.lobby.domain.Role; @@ -17,20 +16,34 @@ import java.util.List; import org.springframework.stereotype.Component; +/** + * Helper class responsible for creating Lobby and GameStateDto, used by RestorationService. + */ @Component public class RestorationMapper { + /** + * Exposed method to generate a Lobby object. + * + * @param lobbyEntity the lobby entry from the database + * @return a Lobby object to be used by RestorationService + */ public Lobby mapToLobby(LobbyEntity lobbyEntity) { String lobbyCode = lobbyEntity.getLobbyCode(); List playerDtoList = mapToPlayerDto(lobbyEntity); - Lobby lobby = buildLobby(lobbyCode, playerDtoList); - return lobby; + return buildLobby(lobbyCode, playerDtoList); } + /** + * Exposed method to generate a GameStateDto object. + * + * @param lobbyEntity the lobby entry from the database + * @return a Lobby object to be used by RestorationService + */ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { GameStateEntity gameStateEntity = lobbyEntity.getGameStateEntity(); if (gameStateEntity == null) { - throw new RuntimeException("Game state entity is null"); + throw new IllegalStateException("Game state entity is null"); } Team winner = null; @@ -99,7 +112,7 @@ private Lobby buildLobby(String lobbyCode, List players) { private static String findHostUsername(List players) { String hostUsername = ""; for (PlayerDto playerDto : players) { - if (playerDto.isHost()){ + if (playerDto.isHost()) { hostUsername = playerDto.username(); } } diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java index e546663d..ee930bc9 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java @@ -9,9 +9,12 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Lobby; import jakarta.annotation.PostConstruct; -import org.springframework.transaction.annotation.Transactional; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +/** + * Service class responsible for re-creating all lobbies and game managers. + */ @Service @Transactional(readOnly = true) public class RestorationService { @@ -22,6 +25,15 @@ public class RestorationService { private final GameService gameService; private final GameManagerFactory gameManagerFactory; + /** + * Constructor for the service class. + * + * @param restorationMapper the helper class used to create Lobby and GameStateDto + * @param lobbyRepository the lobby entry from the database + * @param lobbyService service class responsible for adding a lobby to a list of active lobbies + * @param gameService service class responsible for binding a gameManager to a lobby + * @param gameManagerFactory factory class responsible for creating a gameManager + */ public RestorationService(RestorationMapper restorationMapper, LobbyRepository lobbyRepository, LobbyService lobbyService, GameService gameService, GameManagerFactory gameManagerFactory) { this.restorationMapper = restorationMapper; @@ -31,6 +43,10 @@ public RestorationService(RestorationMapper restorationMapper, LobbyRepository l this.gameManagerFactory = gameManagerFactory; } + /** + * Using PostConstruct we automatically call this method when the container restarts. + */ + // @PostConstruct methods technically gets called right after the beans are initialized @PostConstruct public void restoreOnContainerStart() { for (LobbyEntity lobbyEntity : lobbyRepository.findAll()) { From 6f07d984618a1e122732b81830786ef68f898c32 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 16:01:55 +0200 Subject: [PATCH 100/172] fix: replace Transactional annotation with TransactionTemplate With PostConstruct we cannot ensure that all post processing is done, so there can be no Transactions. The solution is to use TransactionTemplate. Solution found on StackOverflow: https://stackoverflow.com/a/18790494 --- .../restoration/RestorationService.java | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java index ee930bc9..bf9fb5b1 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java @@ -10,13 +10,13 @@ import com.codenames.codenames.backend.lobby.domain.Lobby; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; -/** - * Service class responsible for re-creating all lobbies and game managers. - */ +/** Service class responsible for re-creating all lobbies and game managers. */ @Service -@Transactional(readOnly = true) public class RestorationService { private final RestorationMapper restorationMapper; private final LobbyRepository lobbyRepository; @@ -25,6 +25,8 @@ public class RestorationService { private final GameService gameService; private final GameManagerFactory gameManagerFactory; + private final PlatformTransactionManager transactionManager; + /** * Constructor for the service class. * @@ -34,30 +36,46 @@ public class RestorationService { * @param gameService service class responsible for binding a gameManager to a lobby * @param gameManagerFactory factory class responsible for creating a gameManager */ - public RestorationService(RestorationMapper restorationMapper, LobbyRepository lobbyRepository, - LobbyService lobbyService, GameService gameService, GameManagerFactory gameManagerFactory) { + public RestorationService( + RestorationMapper restorationMapper, + LobbyRepository lobbyRepository, + LobbyService lobbyService, + GameService gameService, + GameManagerFactory gameManagerFactory, + PlatformTransactionManager transactionManager) { this.restorationMapper = restorationMapper; this.lobbyRepository = lobbyRepository; this.lobbyService = lobbyService; this.gameService = gameService; this.gameManagerFactory = gameManagerFactory; + this.transactionManager = transactionManager; } - /** - * Using PostConstruct we automatically call this method when the container restarts. - */ + /** Using PostConstruct we automatically call this method when the container restarts. */ // @PostConstruct methods technically gets called right after the beans are initialized + // Turns out you cannot have PostConstruct and Transactional annotation together + // Solution is to use Transaction Template --> Solution found on StackOverflow @PostConstruct public void restoreOnContainerStart() { - for (LobbyEntity lobbyEntity : lobbyRepository.findAll()) { - String lobbyCode = lobbyEntity.getLobbyCode(); - Lobby lobbyObj = restorationMapper.mapToLobby(lobbyEntity); - lobbyService.restoreLobby(lobbyCode, lobbyObj); + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setReadOnly(true); + + transactionTemplate.execute( + new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + for (LobbyEntity lobbyEntity : lobbyRepository.findAll()) { + String lobbyCode = lobbyEntity.getLobbyCode(); + + Lobby lobbyObj = restorationMapper.mapToLobby(lobbyEntity); + lobbyService.restoreLobby(lobbyCode, lobbyObj); - GameStateDto gameStateDtoObj = restorationMapper.mapToGameStateDto(lobbyEntity); - GameManager gameManagerObj = gameManagerFactory.createFromSnapshot(gameStateDtoObj); - gameService.restoreGameManager(lobbyCode, gameManagerObj); - } + GameStateDto gameStateDtoObj = restorationMapper.mapToGameStateDto(lobbyEntity); + GameManager gameManagerObj = gameManagerFactory.createFromSnapshot(gameStateDtoObj); + gameService.restoreGameManager(lobbyCode, gameManagerObj); + } + } + }); } } From ca095d0e07579f75982de6da0619f46799d42510 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 17:36:13 +0200 Subject: [PATCH 101/172] test: add unit test for mapping to lobby --- .../restoration/RestorationMapperTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java new file mode 100644 index 00000000..4e40cdda --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -0,0 +1,46 @@ +package com.codenames.codenames.backend.database.restoration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.codenames.codenames.backend.database.entity.CardEntity; +import com.codenames.codenames.backend.database.entity.GameStateEntity; +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.entity.PlayerEntity; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RestorationMapperTest { + private RestorationMapper restorationMapper; + private LobbyEntity lobbyEntity; + + @BeforeEach + void setUp() { + restorationMapper = new RestorationMapper(); + lobbyEntity = new LobbyEntity(); + lobbyEntity.setLobbyCode("ABCDE"); + } + + @Test + void testMapToLobbyDto() { + PlayerEntity playerEntity = new PlayerEntity(); + playerEntity.setLobbyEntity(lobbyEntity); + playerEntity.setUsername("Player1"); + playerEntity.setIsHost(true); + playerEntity.setTeam("RED"); + playerEntity.setRole("OPERATIVE"); + List playerList = List.of(playerEntity); + lobbyEntity.setPlayerEntities(playerList); + + Lobby lobby = restorationMapper.mapToLobby(lobbyEntity); + + assertNotNull(lobby); + assertEquals("ABCDE", lobby.getLobbyCode()); + + assertEquals(1, lobby.getPlayerList().size()); + } + + +} From beb0791d125eca602f1fe070876ae5b409e62825 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 18:14:45 +0200 Subject: [PATCH 102/172] refactor: change for loop to ease testing --- .../backend/database/restoration/RestorationMapper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 470175cf..834c125c 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -61,8 +61,7 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { private List mapToCardDto(LobbyEntity lobbyEntity) { List cardList = new ArrayList<>(25); - for (int i = 0; i < 25; i++) { - CardEntity currentCardEntity = lobbyEntity.getCardEntities().get(i); + for (CardEntity currentCardEntity : lobbyEntity.getCardEntities()) { CardDto cardDto = new CardDto( currentCardEntity.getWord(), From 4c42c2ea3df9016f411aedd46d13683f8f47022e Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 18:15:21 +0200 Subject: [PATCH 103/172] test: add unit test for mapping to game state dto --- .../restoration/RestorationMapperTest.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 4e40cdda..627f0bce 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -2,12 +2,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; import com.codenames.codenames.backend.database.entity.CardEntity; import com.codenames.codenames.backend.database.entity.GameStateEntity; import com.codenames.codenames.backend.database.entity.LobbyEntity; import com.codenames.codenames.backend.database.entity.PlayerEntity; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,6 +22,9 @@ class RestorationMapperTest { private RestorationMapper restorationMapper; private LobbyEntity lobbyEntity; + private final Team redTeam = Team.RED; + private final Color redColor = Color.RED; + private final Role operativeRole = Role.OPERATIVE; @BeforeEach void setUp() { @@ -42,5 +52,29 @@ void testMapToLobbyDto() { assertEquals(1, lobby.getPlayerList().size()); } - + @Test + void testMapToGameDto() { + GameStateEntity gameStateEntity = new GameStateEntity(); + gameStateEntity.setCurrentTurn("RED"); + gameStateEntity.setCurrentPhase("OPERATIVE"); + gameStateEntity.setClueWord("TestClueWord"); + gameStateEntity.setClueGuessAmount(3); + lobbyEntity.setGameStateEntity(gameStateEntity); + + CardEntity card = new CardEntity(); + card.setWord("TestCardWord"); + card.setColor("RED"); + card.setIsGuessed(false); + lobbyEntity.setCardEntities(List.of(card)); + + GameStateDto gameStateDto = restorationMapper.mapToGameStateDto(lobbyEntity); + + assertNotNull(gameStateDto); + assertEquals(redTeam, gameStateDto.currentTurn()); + assertEquals(operativeRole, gameStateDto.currentPhase()); + assertEquals("TestClueWord", gameStateDto.currentClue().word()); + assertEquals(1, gameStateDto.cardList().size()); + assertEquals("TestCardWord", gameStateDto.cardList().get(0).word()); + assertEquals(redColor, gameStateDto.cardList().get(0).color()); + } } From 7a6806c59ae9b48c8abeee6f583829334ab8b32b Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 18:26:46 +0200 Subject: [PATCH 104/172] test: add test for null game state --- .../database/restoration/RestorationMapperTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 627f0bce..ea96cfd2 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -77,4 +77,13 @@ void testMapToGameDto() { assertEquals("TestCardWord", gameStateDto.cardList().get(0).word()); assertEquals(redColor, gameStateDto.cardList().get(0).color()); } + + @Test + void testMapToGameDto_null() { + when(lobbyEntity.getCardEntities()).thenReturn(null); + assertThrows( + IllegalStateException.class, () -> restorationMapper.mapToGameStateDto(lobbyEntity)); + } + + } From c98c67cfcac2049fff74347b99057ad933592281 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 18:26:59 +0200 Subject: [PATCH 105/172] test: add null test for clue --- .../restoration/RestorationMapperTest.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index ea96cfd2..3e814a5f 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -80,10 +80,23 @@ void testMapToGameDto() { @Test void testMapToGameDto_null() { - when(lobbyEntity.getCardEntities()).thenReturn(null); + when(lobbyEntity.getGameStateEntity()).thenReturn(null); assertThrows( IllegalStateException.class, () -> restorationMapper.mapToGameStateDto(lobbyEntity)); } + @Test + void testMapToGameDto_nullClueWord() { + GameStateEntity gameStateEntity = new GameStateEntity(); + gameStateEntity.setCurrentTurn("RED"); + gameStateEntity.setCurrentPhase("OPERATIVE"); + gameStateEntity.setClueWord(null); + gameStateEntity.setClueGuessAmount(3); + lobbyEntity.setGameStateEntity(gameStateEntity); + + GameStateDto gameStateDto = restorationMapper.mapToGameStateDto(lobbyEntity); + assertNull(gameStateDto.currentClue()); + } + } From 3501e1fcb458d5cd38e73cad8519a6335b0b1851 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 21:30:11 +0200 Subject: [PATCH 106/172] fix: add null guards to prevent null pointer Found bug through writing test --- .../database/restoration/RestorationMapper.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 834c125c..996ca3ff 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -77,11 +77,19 @@ private List mapToPlayerDto(LobbyEntity lobbyEntity) { for (int i = 0; i < lobbyEntity.getPlayerEntities().size(); i++) { PlayerEntity playerEntity = lobbyEntity.getPlayerEntities().get(i); + Team team = null; + if (playerEntity.getTeam() != null) { + team = Team.valueOf(playerEntity.getTeam()); + } + Role role = null; + if (playerEntity.getRole() != null) { + role = Role.valueOf(playerEntity.getRole()); + } PlayerDto playerDto = new PlayerDto( playerEntity.getUsername(), - Team.valueOf(playerEntity.getTeam()), - Role.valueOf(playerEntity.getRole()), + team, + role, playerEntity.getIsHost()); playerList.add(playerDto); } From 086610ae0a2d8c56facd6124b785159189225b58 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 21:34:05 +0200 Subject: [PATCH 107/172] test: add test for scenario where we have null entries for team --- .../restoration/RestorationMapperTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 3e814a5f..11dcf457 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -98,5 +98,28 @@ void testMapToGameDto_nullClueWord() { assertNull(gameStateDto.currentClue()); } + @Test + void testBuildLobby_nullTeam() { + PlayerEntity playerEntity = new PlayerEntity(); + playerEntity.setLobbyEntity(lobbyEntity); + playerEntity.setUsername("TestPlayer"); + playerEntity.setIsHost(true); + playerEntity.setTeam(redTeam.toString()); + playerEntity.setRole(operativeRole.toString()); + + PlayerEntity playerEntity2 = new PlayerEntity(); + playerEntity2.setLobbyEntity(lobbyEntity); + playerEntity2.setUsername("TestPlayer2"); + playerEntity2.setIsHost(false); + playerEntity2.setTeam(null); + playerEntity2.setRole(operativeRole.toString()); + List playerList = List.of(playerEntity, playerEntity2); + + lobbyEntity.setPlayerEntities(playerList); + + Lobby lobby = restorationMapper.mapToLobby(lobbyEntity); + + assertNull(lobby.getPlayerTeam("TestPlayer2")); + } } From de210ddec41c4ae1d173ffb131716c7304205802 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 21:34:17 +0200 Subject: [PATCH 108/172] Terminal test: add test for scenario where we have null entries for role --- .../restoration/RestorationMapperTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 11dcf457..d46b44f1 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -122,4 +122,28 @@ void testBuildLobby_nullTeam() { assertNull(lobby.getPlayerTeam("TestPlayer2")); } + @Test + void testBuildLobby_nullRole() { + PlayerEntity playerEntity = new PlayerEntity(); + playerEntity.setLobbyEntity(lobbyEntity); + playerEntity.setUsername("TestPlayer"); + playerEntity.setIsHost(true); + playerEntity.setTeam(redTeam.toString()); + playerEntity.setRole(operativeRole.toString()); + + PlayerEntity playerEntity2 = new PlayerEntity(); + playerEntity2.setLobbyEntity(lobbyEntity); + playerEntity2.setUsername("TestPlayer2"); + playerEntity2.setIsHost(false); + playerEntity2.setTeam(redTeam.toString()); + playerEntity2.setRole(null); + List playerList = List.of(playerEntity, playerEntity2); + + lobbyEntity.setPlayerEntities(playerList); + + Lobby lobby = restorationMapper.mapToLobby(lobbyEntity); + + assertNull(lobby.getPlayerRole("TestPlayer2")); + } + } From 0b037d4d75d797f1854274b2ae0f353fa6eb07c2 Mon Sep 17 00:00:00 2001 From: XtophB Date: Tue, 9 Jun 2026 22:45:33 +0200 Subject: [PATCH 109/172] test: add unit test for restorationService class --- .../restoration/RestorationMapperTest.java | 1 - .../restoration/RestorationServiceTest.java | 89 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index d46b44f1..2c7c42a1 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -80,7 +80,6 @@ void testMapToGameDto() { @Test void testMapToGameDto_null() { - when(lobbyEntity.getGameStateEntity()).thenReturn(null); assertThrows( IllegalStateException.class, () -> restorationMapper.mapToGameStateDto(lobbyEntity)); } diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java new file mode 100644 index 00000000..aa729c62 --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java @@ -0,0 +1,89 @@ +package com.codenames.codenames.backend.database.restoration; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.codenames.codenames.backend.database.entity.LobbyEntity; +import com.codenames.codenames.backend.database.repository.LobbyRepository; +import com.codenames.codenames.backend.game.api.dto.CardDto; +import com.codenames.codenames.backend.game.api.dto.ClueDto; +import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.GameManager; +import com.codenames.codenames.backend.game.domain.GameManagerFactory; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; + +class RestorationServiceTest { + private RestorationService restorationService; + private RestorationMapper restorationMapper; + private GameManagerFactory gameManagerFactory; + private LobbyService lobbyService; + private LobbyRepository lobbyRepository; + private GameService gameService; + private TransactionStatus transactionStatus; + private PlatformTransactionManager transactionManager; + private GameStateDto gameStateDto; + private List cardDtoList; + private Lobby mockLobby; + private GameManager mockGameManager; + + private static final Team redTeam = Team.RED; + private static final Role spymaster = Role.SPYMASTER; + + @BeforeEach + void setUp() { + restorationMapper = mock(RestorationMapper.class); + lobbyService = mock(LobbyService.class); + lobbyRepository = mock(LobbyRepository.class); + gameManagerFactory = mock(GameManagerFactory.class); + gameService = mock(GameService.class); + transactionManager = mock(PlatformTransactionManager.class); + transactionStatus = mock(TransactionStatus.class); + + + mockLobby = mock(Lobby.class); + mockGameManager = mock(GameManager.class); + + cardDtoList = List.of(new CardDto("TEST", null, false)); + gameStateDto = + new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), cardDtoList); + + when(transactionManager.getTransaction(any())).thenReturn(transactionStatus); + + restorationService = + new RestorationService( + restorationMapper, + lobbyRepository, + lobbyService, + gameService, + gameManagerFactory, + transactionManager); + } + + @Test + void testRestoreOnContainerStart() { + LobbyEntity lobbyEntity = new LobbyEntity(); + lobbyEntity.setLobbyCode("ABCDE"); + when(lobbyRepository.findAll()).thenReturn(List.of(lobbyEntity)); + + when(restorationMapper.mapToGameStateDto(lobbyEntity)).thenReturn(gameStateDto); + when(restorationMapper.mapToLobby(lobbyEntity)).thenReturn(mockLobby); + when(gameManagerFactory.createFromSnapshot(gameStateDto)).thenReturn(mockGameManager); + + restorationService.restoreOnContainerStart(); + + verify(lobbyService, times(1)).restoreLobby("ABCDE", mockLobby); + verify(gameService, times(1)).restoreGameManager("ABCDE", mockGameManager); + } +} From 8a4c92babf0629aab8f85ea51d4eb059714a25d6 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 10 Jun 2026 00:47:37 +0200 Subject: [PATCH 110/172] feat: add logging to ease bug findings --- .../backend/database/persistence/PersistenceMapper.java | 5 +++-- .../backend/database/persistence/PersistenceService.java | 1 + .../backend/database/restoration/RestorationService.java | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index c3d54a2b..7e0531d9 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -10,12 +10,14 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import java.util.ArrayList; import java.util.List; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * Helper class that is called by the PersistenceService to map all the tables when a snapshot is * created. */ +@Slf4j @Component public class PersistenceMapper { /** @@ -28,7 +30,6 @@ public class PersistenceMapper { */ public LobbyEntity mapAggregateParentLobbyEntity( String lobbyCode, GameManager gameManager, List players) { - LobbyEntity lobbyEntity = new LobbyEntity(); lobbyEntity.setLobbyCode(lobbyCode); @@ -37,7 +38,7 @@ public LobbyEntity mapAggregateParentLobbyEntity( lobbyEntity.setGameStateEntity(mapGameState(lobbyEntity, lobbyCode, gameManager)); lobbyEntity.setCardEntities(mapCard(lobbyEntity, cardList)); lobbyEntity.setPlayerEntities(mapPlayer(lobbyEntity, players)); - + log.info("Mapping has been completed"); return lobbyEntity; } diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java index bf218cb8..ac49e54e 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -49,5 +49,6 @@ public void saveSnapShot(String lobbyCode) { LobbyEntity lobbySnapShot = persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerList); lobbyRepository.save(lobbySnapShot); + log.info("Snapshot has been saved to DB"); } } diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java index bf9fb5b1..e7b85423 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationService.java @@ -9,6 +9,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Lobby; import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; @@ -16,6 +17,7 @@ import org.springframework.transaction.support.TransactionTemplate; /** Service class responsible for re-creating all lobbies and game managers. */ +@Slf4j @Service public class RestorationService { private final RestorationMapper restorationMapper; @@ -69,11 +71,15 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { String lobbyCode = lobbyEntity.getLobbyCode(); Lobby lobbyObj = restorationMapper.mapToLobby(lobbyEntity); + log.info("Attempting to restore lobby: {}", lobbyCode); lobbyService.restoreLobby(lobbyCode, lobbyObj); + log.info("Lobby has been restored: {}", lobbyCode); GameStateDto gameStateDtoObj = restorationMapper.mapToGameStateDto(lobbyEntity); GameManager gameManagerObj = gameManagerFactory.createFromSnapshot(gameStateDtoObj); + log.info("Attempting to restore game: {}", lobbyCode); gameService.restoreGameManager(lobbyCode, gameManagerObj); + log.info("Game has been restored: {}", lobbyCode); } } }); From 301992f7137cf1e5c648934312b02b843e89538f Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 10 Jun 2026 17:48:28 +0200 Subject: [PATCH 111/172] refactor: remove unused import --- .../backend/database/restoration/RestorationMapperTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 2c7c42a1..6c763eff 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; import com.codenames.codenames.backend.database.entity.CardEntity; import com.codenames.codenames.backend.database.entity.GameStateEntity; From 49a10b32631d32cb574e68aa80f4415a686bfc87 Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:20:07 +0200 Subject: [PATCH 112/172] Implement backend cheat functionality --- .../backend/chat/api/dto/ChatMessageType.java | 5 +- .../game/api/GameSocketController.java | 52 +++++++++++- .../game/api/dto/CheatCardMessage.java | 16 ++++ .../backend/game/api/dto/GameStateDto.java | 5 +- .../backend/game/application/GameService.java | 18 +++++ .../backend/game/domain/CheatResult.java | 8 ++ .../backend/game/domain/GameManager.java | 79 +++++++++++++++++++ .../mapping/DataTransferObjectService.java | 8 +- 8 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/codenames/codenames/backend/game/api/dto/CheatCardMessage.java create mode 100644 src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java diff --git a/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java index c6f78b84..07dcf5ef 100644 --- a/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java +++ b/src/main/java/com/codenames/codenames/backend/chat/api/dto/ChatMessageType.java @@ -4,5 +4,6 @@ * Enum representing the type of chat message. */ public enum ChatMessageType { - CHAT -} + CHAT, + SYSTEM +} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 7d00a4c6..17a1878a 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -1,12 +1,19 @@ package com.codenames.codenames.backend.game.api; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.database.persistence.PersistenceService; +import com.codenames.codenames.backend.game.api.dto.CheatCardMessage; import com.codenames.codenames.backend.game.api.dto.ClueMessage; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.CheatResult; import com.codenames.codenames.backend.game.domain.Clue; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @@ -21,6 +28,7 @@ public class GameSocketController { private final GameService gameService; + private final LobbyService lobbyService; private final SimpMessagingTemplate messagingTemplate; private final PersistenceService persistenceService; @@ -33,15 +41,57 @@ public class GameSocketController { * @param gameService service responsible for gameplay logic * @param messagingTemplate template used for broadcasting websocket messages * @param persistenceService service used to persist current backend state + * @param lobbyService service responsible for lobby and player information */ public GameSocketController( GameService gameService, SimpMessagingTemplate messagingTemplate, - PersistenceService persistenceService) { + PersistenceService persistenceService, + LobbyService lobbyService) { this.gameService = gameService; this.messagingTemplate = messagingTemplate; this.persistenceService = persistenceService; + this.lobbyService = lobbyService; + } + + /** + * Handles a cheat request and sends the result only to the requesting player. + * + * @param message the cheat request containing lobby, username and selected cards + */ + @MessageMapping("/cheat") + public void useCheat(CheatCardMessage message) { + + Team team = lobbyService.getPlayerTeam(message.getUsername(), message.getLobbyCode()); + Role role = lobbyService.getPlayerRole(message.getUsername(), message.getLobbyCode()); + + if (team == null || role != Role.OPERATIVE) { + return; + } + + CheatResult result = + gameService.useCheat( + message.getLobbyCode(), + message.getPositions(), + team); + + if (result == null) { + return; + } + + ChatDto systemMessage = + new ChatDto( + "System", + result.message(), + ChatMessageType.SYSTEM); + + messagingTemplate.convertAndSendToUser( + message.getUsername(), + "/queue/system", + systemMessage); + + persistenceService.saveSnapShot(message.getLobbyCode()); } /** diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/CheatCardMessage.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/CheatCardMessage.java new file mode 100644 index 00000000..dba6690f --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/CheatCardMessage.java @@ -0,0 +1,16 @@ +package com.codenames.codenames.backend.game.api.dto; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +/** + * WebSocket message for requesting a cheat check on selected cards. + */ +@Getter +@Setter +public class CheatCardMessage { + private String lobbyCode; + private String username; + private List positions; +} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java index c5167d1c..ae38b5dd 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java @@ -18,4 +18,7 @@ public record GameStateDto( Team currentTurn, Role currentPhase, ClueDto currentClue, - List cardList) {} + List cardList, + boolean redTeamCheatUsed, + boolean blueTeamCheatUsed) { +} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java index cdb57463..fc7df620 100644 --- a/src/main/java/com/codenames/codenames/backend/game/application/GameService.java +++ b/src/main/java/com/codenames/codenames/backend/game/application/GameService.java @@ -1,11 +1,13 @@ package com.codenames.codenames.backend.game.application; import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.domain.CheatResult; import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.GameManager; import com.codenames.codenames.backend.game.domain.GameManagerFactory; import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; @@ -162,4 +164,20 @@ public Map getGameSnapshots() { .collect( java.util.stream.Collectors.toMap(lobbyCode -> lobbyCode, this::getCurrentGameState)); } + + /** + * Performs a cheat request for the given team. + * + * @param lobbyCode the lobby code of the game + * @param positions the selected card positions + * @param team the team requesting the cheat + * @return the cheat result or null if the request is invalid + */ + public CheatResult useCheat( + String lobbyCode, + List positions, + Team team) { + + return getGame(lobbyCode).useCheat(positions, team); + } } diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java b/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java new file mode 100644 index 00000000..6d5b57d1 --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java @@ -0,0 +1,8 @@ +package com.codenames.codenames.backend.game.domain; + +/** + * Result of a cheat attempt. + * + * @param message private message for the player + */ +public record CheatResult(String message) {} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 2a2b98aa..0227276c 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -35,6 +35,9 @@ public class GameManager { @Getter private Team currentTurn; @Getter private Role currentPhase; + @Getter private boolean redTeamCheatUsed = false; + @Getter private boolean blueTeamCheatUsed = false; + /** * Constructor for a new GameManager and initializes the playing board. * @@ -83,6 +86,8 @@ public GameManager( this.currentTurn = state.currentTurn(); this.currentPhase = state.currentPhase(); this.winner = state.winner(); + this.redTeamCheatUsed = state.redTeamCheatUsed(); + this.blueTeamCheatUsed = state.blueTeamCheatUsed(); this.currentClue = state.currentClue() == null ? null @@ -303,4 +308,78 @@ private void checkCorrectTurn(Team team, Role role) { throw new IllegalStateException("Not your turn/ role"); } } + + /** + * Checks whether a card position is within the valid board range. + * + * @param position the position to validate + * @return true if the position is valid + */ + private boolean isValidPosition(Integer position) { + return position != null + && position >= 0 + && position < board.getCardList().size(); + } + + /** + * Performs the cheat action for the current team. + * The team may use the cheat only once per game. + * If at least one selected card belongs to the team, + * one correct card is returned. + * + * @param positions selected card positions + * @param team the team requesting the cheat + * @return the cheat result or null if the request is invalid + */ + public CheatResult useCheat(List positions, Team team) { + + if (positions == null || positions.isEmpty()) { + return null; + } + + if (team != currentTurn || currentPhase != Role.OPERATIVE) { + return null; + } + + if (team == Team.RED && redTeamCheatUsed) { + return null; + } + + if (team == Team.BLUE && blueTeamCheatUsed) { + return null; + } + + List selectedCards = + positions.stream() + .filter(this::isValidPosition) + .filter(position -> !board.getIsGuessed(position)) + .map(position -> board.getCardList().get(position)) + .toList(); + + if (selectedCards.isEmpty()) { + return null; + } + + List correctCards = + selectedCards.stream() + .filter( + card -> + (team == Team.RED && card.getColor() == Color.RED) + || (team == Team.BLUE && card.getColor() == Color.BLUE)) + .toList(); + + if (team == Team.RED) { + redTeamCheatUsed = true; + } else { + blueTeamCheatUsed = true; + } + + if (correctCards.isEmpty()) { + return new CheatResult("Keine der ausgewählten Karten ist richtig."); + } + + Card correctCard = correctCards.get(0); + + return new CheatResult("Die Karte \"" + correctCard.getWord() + "\" ist richtig."); + } } diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index a6b0c598..387089d8 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -53,7 +53,9 @@ public GameStateDto createGameStateDto( currentTurn, currentPhase, null, - cardDataTransferObject); + cardDataTransferObject, + gameManager.isRedTeamCheatUsed(), + gameManager.isBlueTeamCheatUsed()); } String word = gameManager.getCurrentClue().word(); int guessAmount = gameManager.getCurrentClue().guessAmount(); @@ -62,6 +64,8 @@ public GameStateDto createGameStateDto( currentTurn, currentPhase, new ClueDto(word, guessAmount), - cardDataTransferObject); + cardDataTransferObject, + gameManager.isRedTeamCheatUsed(), + gameManager.isBlueTeamCheatUsed()); } } From 1f8862b5666984df3510e499f7af33f695f57fac Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:55:14 +0200 Subject: [PATCH 113/172] Implement backend cheat validation tests --- .../game/api/GameSocketControllerTest.java | 42 +++++++++- .../game/application/GameServiceTest.java | 18 ++++- .../game/domain/GameManagerFactoryTest.java | 6 +- .../backend/game/domain/GameManagerTest.java | 79 +++++++++++++++++-- .../lobby/api/LobbySocketControllerTest.java | 2 +- .../SystemStateRecoveryServiceTest.java | 4 +- .../infrastructure/JsonStateStoreTest.java | 2 +- .../serialization/SerializationJsonTest.java | 13 ++- 8 files changed, 148 insertions(+), 18 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index ea9e8e13..5649ac9b 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -2,17 +2,24 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.codenames.codenames.backend.chat.api.dto.ChatDto; +import com.codenames.codenames.backend.chat.api.dto.ChatMessageType; import com.codenames.codenames.backend.database.persistence.PersistenceService; +import com.codenames.codenames.backend.game.api.dto.CheatCardMessage; import com.codenames.codenames.backend.game.api.dto.ClueMessage; import com.codenames.codenames.backend.game.api.dto.GameStateDto; import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; import com.codenames.codenames.backend.game.application.GameService; +import com.codenames.codenames.backend.game.domain.CheatResult; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -34,11 +41,18 @@ class GameSocketControllerTest { @Mock private PersistenceService persistenceService; + @Mock private LobbyService lobbyService; + private GameSocketController controller; @BeforeEach void setUp() { - controller = new GameSocketController(gameService, messagingTemplate, persistenceService); + controller = + new GameSocketController( + gameService, + messagingTemplate, + persistenceService, + lobbyService); } @Test @@ -106,6 +120,30 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { } private GameStateDto createGameStateDto() { - return new GameStateDto(null, Team.RED, null, null, List.of()); + return new GameStateDto(null, Team.RED, null, null, List.of(), false, false); + } + + @Test + void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { + CheatCardMessage message = new CheatCardMessage(); + message.setLobbyCode(LOBBY_CODE); + message.setUsername("Max"); + message.setPositions(List.of(0, 1)); + + CheatResult result = new CheatResult("Die Karte \"Dog\" ist richtig."); + + when(lobbyService.getPlayerTeam("Max", LOBBY_CODE)).thenReturn(Team.RED); + when(lobbyService.getPlayerRole("Max", LOBBY_CODE)).thenReturn(Role.OPERATIVE); + when(gameService.useCheat(LOBBY_CODE, List.of(0, 1), Team.RED)).thenReturn(result); + + controller.useCheat(message); + + verify(messagingTemplate) + .convertAndSendToUser( + eq("Max"), + eq("/queue/system"), + eq(new ChatDto("System", result.message(), ChatMessageType.SYSTEM))); + + verify(persistenceService).saveSnapShot(LOBBY_CODE); } } diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index b5a49e72..e803f62a 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -12,6 +12,7 @@ import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; +import com.codenames.codenames.backend.game.domain.CheatResult; import com.codenames.codenames.backend.game.domain.Clue; import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.game.domain.GameManager; @@ -114,7 +115,7 @@ void testGetCurrentGameState() { redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), - List.of(new CardDto("Dog", Color.RED, false))); + List.of(new CardDto("Dog", Color.RED, false)), false, false); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); when(mockGameManager.getCurrentPhase()).thenReturn(Role.SPYMASTER); when(mockGameManager.getRemainingGuesses()).thenReturn(2); @@ -144,7 +145,7 @@ void getGameSnapshotsShouldReturnAllCurrentGameStates() { redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), - List.of(new CardDto("Dog", Color.RED, false))); + List.of(new CardDto("Dog", Color.RED, false)), false, false); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); when(mockGameManager.getCurrentPhase()).thenReturn(Role.SPYMASTER); @@ -156,4 +157,17 @@ void getGameSnapshotsShouldReturnAllCurrentGameStates() { assertEquals(1, snapshots.size()); assertEquals(expected, snapshots.get(lobbyCode)); } + + @Test + void testUseCheat() { + List positions = List.of(0, 1); + CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig."); + + when(mockGameManager.useCheat(positions, redTeam)).thenReturn(expected); + + CheatResult result = gameService.useCheat(lobbyCode, positions, redTeam); + + assertEquals(expected, result); + verify(mockGameManager, times(1)).useCheat(positions, redTeam); + } } diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 29500811..1285223e 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -44,7 +44,7 @@ void testCreateFromSnapshotWithClue() { Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), - List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); + List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false)), false, false); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -62,7 +62,7 @@ void testCreateFromSnapshotWithClue() { void testCreateFromSnapshotWithoutClue() { GameStateDto snapshot = new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false)), false, false); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -84,7 +84,7 @@ void testCreateFromSnapshotMapsCardsAndCountsGuessedCardsByColor() { new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.RED, false), new CardDto("Tree", Color.BLUE, true), - new CardDto("Sun", Color.NEUTRAL, true))); + new CardDto("Sun", Color.NEUTRAL, true)), false, false); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 0725ef67..884de478 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -335,7 +335,7 @@ void testPassTurn_throwsWhenSpymaster() { @Test void recoveryConstructorThrowsWhenCardsAreNull() { GameStateDto state = - new GameStateDto(null, Team.RED, Role.SPYMASTER, null, null); + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, null, false, false); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -344,7 +344,7 @@ void recoveryConstructorThrowsWhenCardsAreNull() { @Test void recoveryConstructorThrowsWhenCardsAreEmpty() { GameStateDto state = - new GameStateDto(null, Team.RED, Role.SPYMASTER, null, List.of()); + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, List.of(), false, false); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -356,7 +356,7 @@ void recoveryConstructorThrowsWhenCurrentTurnIsNull() { List.of(new CardDto("Dog", Color.RED, false)); GameStateDto state = - new GameStateDto(null, null, Role.SPYMASTER, null, cards); + new GameStateDto(null, null, Role.SPYMASTER, null, cards, false, false); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -368,7 +368,7 @@ void recoveryConstructorThrowsWhenCurrentPhaseIsNull() { List.of(new CardDto("Dog", Color.RED, false)); GameStateDto state = - new GameStateDto(null, Team.RED, null, null, cards); + new GameStateDto(null, Team.RED, null, null, cards, false, false); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -383,7 +383,7 @@ void recoveryConstructorRestoresPersistedState() { GameStateDto state = new GameStateDto( - Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), cards); + Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), cards, false, false); GameManager restored = new GameManager(state, mockClueValidationService); @@ -395,4 +395,73 @@ void recoveryConstructorRestoresPersistedState() { assertEquals(0, restored.getCurrentBlueFound()); assertTrue(restored.getCardList().get(0).isGuessed()); } + + @Test + void useCheatReturnsNullWhenNoCardsSelected() { + helperMethodAdvanceTurns(gameManager, 1); + + CheatResult result = gameManager.useCheat(List.of(), redTeam); + + assertNull(result); + } + + @Test + void useCheatReturnsNullWhenNotOperativePhase() { + CheatResult result = gameManager.useCheat(List.of(0), redTeam); + + assertNull(result); + } + + @Test + void useCheatReturnsCorrectCardNameWhenSelectedCardsContainTeamCard() { + mockCardGeneration( + List.of( + new Card("Dog", Color.BLUE), + new Card("Cat", Color.RED), + new Card("Tree", Color.NEUTRAL))); + gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); + helperMethodAdvanceTurns(gameManager, 1); + + CheatResult result = gameManager.useCheat(List.of(0, 1, 2), redTeam); + + assertEquals("Die Karte \"Cat\" ist richtig.", result.message()); + assertTrue(gameManager.isRedTeamCheatUsed()); + } + + @Test + void useCheatReturnsNoCorrectCardMessageWhenNoSelectedCardIsCorrect() { + mockCardGeneration( + List.of( + new Card("Dog", Color.BLUE), + new Card("Tree", Color.NEUTRAL))); + gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); + helperMethodAdvanceTurns(gameManager, 1); + + CheatResult result = gameManager.useCheat(List.of(0, 1), redTeam); + + assertEquals("Keine der ausgewählten Karten ist richtig.", result.message()); + assertTrue(gameManager.isRedTeamCheatUsed()); + } + + @Test + void useCheatCanOnlyBeUsedOncePerTeam() { + mockCardGeneration(List.of(new Card("Dog", Color.RED))); + gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); + helperMethodAdvanceTurns(gameManager, 1); + + CheatResult firstResult = gameManager.useCheat(List.of(0), redTeam); + CheatResult secondResult = gameManager.useCheat(List.of(0), redTeam); + + assertEquals("Die Karte \"Dog\" ist richtig.", firstResult.message()); + assertNull(secondResult); + } + + @Test + void useCheatReturnsNullWhenOnlyInvalidPositionsAreSelected() { + helperMethodAdvanceTurns(gameManager, 1); + + CheatResult result = gameManager.useCheat(List.of(-1, 99), redTeam); + + assertNull(result); + } } diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index a9cf4201..7c1eeb06 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -166,6 +166,6 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { } private GameStateDto createGameStatePayload() { - return new GameStateDto(null, null, null, null, List.of()); + return new GameStateDto(null, null, null, null, List.of(), false, false); } } diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 1aabc3ca..8cf2e77c 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -72,7 +72,7 @@ void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), - List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); + List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false)), false, false); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, @@ -208,7 +208,7 @@ void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); GameStateDto gameSnapshot = new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false)), false, false); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 727bc723..84989ba9 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -59,7 +59,7 @@ void saveAndLoadRoundTrip() { GameStateDto gameSnapshot = new GameStateDto( - null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of()); + null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of(), false, false); SystemSnapshot expectedSnapshot = new SystemSnapshot( diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 90356dde..f674c2a0 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -36,7 +36,14 @@ void setUp() { dummyList = List.of(new CardDto("TEST", null, false)); dummyGameState = - new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), dummyList); + new GameStateDto( + redTeam, + redTeam, + spymaster, + new ClueDto("Test", 1), + dummyList, + false, + false); } @Test @@ -44,7 +51,9 @@ void testSerialize_pass() { String expectedResult = "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," + "\"guessAmount\":1},\"cardList\":[{\"word\":\"TEST\"," - + "\"color\":null,\"isGuessed\":false}]}"; + + "\"color\":null,\"isGuessed\":false}]," + + "\"redTeamCheatUsed\":false," + + "\"blueTeamCheatUsed\":false}"; String result = serializer.serialize(dummyGameState); assertEquals(expectedResult, result); } From af26483c1b384443a7760fb60dfb202edd66a7d0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:04:34 +0200 Subject: [PATCH 114/172] feat: add logging of card list before saving to DB --- .../backend/database/persistence/PersistenceService.java | 1 + .../java/com/codenames/codenames/backend/game/domain/Card.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java index ac49e54e..a24cc438 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -45,6 +45,7 @@ public PersistenceService(LobbyService lobbyService, GameService gameService, public void saveSnapShot(String lobbyCode) { log.info("Saving snapshot of lobby: {}", lobbyCode); GameManager gameManager = gameService.getGameState(lobbyCode); + log.info("Card state for lobby {}: {}", lobbyCode, gameManager.getCardList()); List playerList = lobbyService.getPlayersDto(lobbyCode); LobbyEntity lobbySnapShot = persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerList); diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java index 0018cb28..8c15a2bc 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/Card.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/Card.java @@ -1,9 +1,11 @@ package com.codenames.codenames.backend.game.domain; import lombok.Getter; +import lombok.ToString; /** Represents a single card on a board. */ @Getter +@ToString public class Card { private final String word; private final Color color; From 24be06bc9519f45ed361340358e928fcf58026f0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:06:38 +0200 Subject: [PATCH 115/172] fix: add persistence when card is flip --- .../codenames/backend/game/api/GameSocketController.java | 1 + .../codenames/backend/game/api/GameSocketControllerTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index 7d00a4c6..1c3aa35b 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -68,6 +68,7 @@ public void startGame(StartGameMessage message) { public void revealCard(RevealCardMessage message) { gameService.flipCard(message.getLobbyCode(), message.getPosition(), message.getCurrentTurn()); + persistenceService.saveSnapShot(message.getLobbyCode()); messagingTemplate.convertAndSend( GAME_TOPIC_PREFIX + message.getLobbyCode(), diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index ea9e8e13..33e37e24 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -68,6 +68,7 @@ void revealCardShouldPersistAndBroadcastState() { controller.revealCard(message); verify(gameService).flipCard(LOBBY_CODE, 0, Team.RED); + verify(persistenceService).saveSnapShot(LOBBY_CODE); verify(messagingTemplate).convertAndSend(anyString(), any(Object.class)); } From b62af833286ff98d4dccae98fb564f1a06ff0fd8 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:48:45 +0200 Subject: [PATCH 116/172] feat: add mapping for remaining guesses --- .../backend/database/restoration/RestorationMapper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 996ca3ff..38be2bbc 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -54,8 +54,10 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { if (gameStateEntity.getClueWord() != null) { clueDto = new ClueDto(gameStateEntity.getClueWord(), gameStateEntity.getClueGuessAmount()); } + int remainingGuesses = gameStateEntity.getRemainingGuesses() List cardList = mapToCardDto(lobbyEntity); - return new GameStateDto(winner, currentTeam, currentPhase, clueDto, cardList); + return new GameStateDto(winner, currentTeam, currentPhase, clueDto, + remainingGuesses, cardList); } private List mapToCardDto(LobbyEntity lobbyEntity) { From 067acd5e01a7ac9a42b77a1c477d20448d2fbb2e Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:49:18 +0200 Subject: [PATCH 117/172] feature: update dto to support remaining guesses --- .../codenames/codenames/backend/game/api/dto/GameStateDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java index c5167d1c..1e83f8be 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java @@ -18,4 +18,5 @@ public record GameStateDto( Team currentTurn, Role currentPhase, ClueDto currentClue, + int remainingGuesses, List cardList) {} From 9a1aaab5b3d2bb038426bdaa9d8e56ebf7d41a2d Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:49:46 +0200 Subject: [PATCH 118/172] fix: add semicolon back --- .../backend/database/restoration/RestorationMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 38be2bbc..0771e25d 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -54,7 +54,7 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { if (gameStateEntity.getClueWord() != null) { clueDto = new ClueDto(gameStateEntity.getClueWord(), gameStateEntity.getClueGuessAmount()); } - int remainingGuesses = gameStateEntity.getRemainingGuesses() + int remainingGuesses = gameStateEntity.getRemainingGuesses(); List cardList = mapToCardDto(lobbyEntity); return new GameStateDto(winner, currentTeam, currentPhase, clueDto, remainingGuesses, cardList); From 5dfe1e460390591ab60252e785d0f90d679bc75f Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 16:52:56 +0200 Subject: [PATCH 119/172] fix: update existing methods using GameStateDto --- .../codenames/backend/game/domain/GameManager.java | 1 + .../backend/game/mapping/DataTransferObjectService.java | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 2a2b98aa..2ed4e1de 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -87,6 +87,7 @@ public GameManager( state.currentClue() == null ? null : new Clue(state.currentClue().word(), state.currentClue().guessAmount()); + this.remainingGuesses = state.remainingGuesses(); this.clueValidationService = clueValidationService; List cards = state.cardList().stream().map(this::toCard).toList(); diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index a6b0c598..a92aefd2 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -47,13 +47,15 @@ public GameStateDto createGameStateDto( if (gameManager.getWinner() != null) { log.info("A Game is over. Winner is {}.", gameManager.getWinner()); } + int remainingGuesses = gameManager.getRemainingGuesses(); if (gameManager.getCurrentClue() == null) { return new GameStateDto( gameManager.getWinner(), currentTurn, currentPhase, null, - cardDataTransferObject); + remainingGuesses, + cardDataTransferObject); } String word = gameManager.getCurrentClue().word(); int guessAmount = gameManager.getCurrentClue().guessAmount(); @@ -62,6 +64,7 @@ public GameStateDto createGameStateDto( currentTurn, currentPhase, new ClueDto(word, guessAmount), - cardDataTransferObject); + remainingGuesses, + cardDataTransferObject); } } From 9134c834293dca98239fd6571e6019959cfb3489 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 17:02:40 +0200 Subject: [PATCH 120/172] fix: update all tests using gameStateDto --- .../database/restoration/RestorationServiceTest.java | 2 +- .../backend/game/api/GameSocketControllerTest.java | 2 +- .../backend/game/application/GameServiceTest.java | 2 ++ .../backend/game/domain/GameManagerFactoryTest.java | 4 +++- .../backend/game/domain/GameManagerTest.java | 10 +++++----- .../backend/lobby/api/LobbySocketControllerTest.java | 2 +- .../application/SystemStateRecoveryServiceTest.java | 3 ++- .../recovery/infrastructure/JsonStateStoreTest.java | 2 +- .../backend/serialization/SerializationJsonTest.java | 11 ++++++----- 9 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java index aa729c62..97dc1aba 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java @@ -57,7 +57,7 @@ void setUp() { cardDtoList = List.of(new CardDto("TEST", null, false)); gameStateDto = - new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), cardDtoList); + new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), 2, cardDtoList); when(transactionManager.getTransaction(any())).thenReturn(transactionStatus); diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index ea9e8e13..92428320 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -106,6 +106,6 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { } private GameStateDto createGameStateDto() { - return new GameStateDto(null, Team.RED, null, null, List.of()); + return new GameStateDto(null, Team.RED, null, null, 0, List.of()); } } diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index b5a49e72..6089b375 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -114,6 +114,7 @@ void testGetCurrentGameState() { redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), + 3, List.of(new CardDto("Dog", Color.RED, false))); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); when(mockGameManager.getCurrentPhase()).thenReturn(Role.SPYMASTER); @@ -144,6 +145,7 @@ void getGameSnapshotsShouldReturnAllCurrentGameStates() { redTeam, Role.SPYMASTER, new ClueDto("ANIMAL", 2), + 3, List.of(new CardDto("Dog", Color.RED, false))); when(mockGameManager.getCurrentTurn()).thenReturn(redTeam); diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 29500811..7c0f3ac3 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -44,6 +44,7 @@ void testCreateFromSnapshotWithClue() { Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), + 3, List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -62,7 +63,7 @@ void testCreateFromSnapshotWithClue() { void testCreateFromSnapshotWithoutClue() { GameStateDto snapshot = new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, 0, List.of(new CardDto("Tree", Color.BLUE, false))); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); @@ -80,6 +81,7 @@ void testCreateFromSnapshotMapsCardsAndCountsGuessedCardsByColor() { Team.RED, Role.OPERATIVE, null, + 0, List.of( new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.RED, false), diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java index 0725ef67..be5c4ddf 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerTest.java @@ -335,7 +335,7 @@ void testPassTurn_throwsWhenSpymaster() { @Test void recoveryConstructorThrowsWhenCardsAreNull() { GameStateDto state = - new GameStateDto(null, Team.RED, Role.SPYMASTER, null, null); + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, 0, null); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -344,7 +344,7 @@ void recoveryConstructorThrowsWhenCardsAreNull() { @Test void recoveryConstructorThrowsWhenCardsAreEmpty() { GameStateDto state = - new GameStateDto(null, Team.RED, Role.SPYMASTER, null, List.of()); + new GameStateDto(null, Team.RED, Role.SPYMASTER, null, 0, List.of()); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -356,7 +356,7 @@ void recoveryConstructorThrowsWhenCurrentTurnIsNull() { List.of(new CardDto("Dog", Color.RED, false)); GameStateDto state = - new GameStateDto(null, null, Role.SPYMASTER, null, cards); + new GameStateDto(null, null, Role.SPYMASTER, null, 0, cards); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -368,7 +368,7 @@ void recoveryConstructorThrowsWhenCurrentPhaseIsNull() { List.of(new CardDto("Dog", Color.RED, false)); GameStateDto state = - new GameStateDto(null, Team.RED, null, null, cards); + new GameStateDto(null, Team.RED, null, null, 0, cards); assertThrows( IllegalArgumentException.class, () -> new GameManager(state, mockClueValidationService)); @@ -383,7 +383,7 @@ void recoveryConstructorRestoresPersistedState() { GameStateDto state = new GameStateDto( - Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), cards); + Team.RED, Team.BLUE, Role.OPERATIVE, new ClueDto("ANIMAL", 2), 3, cards); GameManager restored = new GameManager(state, mockClueValidationService); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index a9cf4201..058e7435 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -166,6 +166,6 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { } private GameStateDto createGameStatePayload() { - return new GameStateDto(null, null, null, null, List.of()); + return new GameStateDto(null, null, null, null, 0, List.of()); } } diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 1aabc3ca..866b4ead 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -72,6 +72,7 @@ void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), + 3, List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( @@ -208,7 +209,7 @@ void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); GameStateDto gameSnapshot = new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false))); + null, Team.BLUE, Role.SPYMASTER, null, 0, List.of(new CardDto("Tree", Color.BLUE, false))); SystemSnapshot snapshot = new SystemSnapshot( SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java index 727bc723..dddeac0a 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java @@ -59,7 +59,7 @@ void saveAndLoadRoundTrip() { GameStateDto gameSnapshot = new GameStateDto( - null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of()); + null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), 3, List.of()); SystemSnapshot expectedSnapshot = new SystemSnapshot( diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java index 90356dde..4282b0de 100644 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java @@ -36,15 +36,16 @@ void setUp() { dummyList = List.of(new CardDto("TEST", null, false)); dummyGameState = - new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), dummyList); + new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), 2, dummyList); } @Test void testSerialize_pass() { - String expectedResult = "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," - + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," - + "\"guessAmount\":1},\"cardList\":[{\"word\":\"TEST\"," - + "\"color\":null,\"isGuessed\":false}]}"; + String expectedResult = + "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," + + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," + + "\"guessAmount\":1},\"remainingGuesses\":2,\"cardList\":[{\"word\":\"TEST\"," + + "\"color\":null,\"isGuessed\":false}]}"; String result = serializer.serialize(dummyGameState); assertEquals(expectedResult, result); } From c9c1aee25234d57d58ca621ae57c82e089ef69ed Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 17:54:38 +0200 Subject: [PATCH 121/172] feat: add DB lobby entry (and all cascades) deletion when lobby is empty --- .../codenames/backend/lobby/application/LobbyService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index dd1454df..fa994a1e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -1,6 +1,7 @@ package com.codenames.codenames.backend.lobby.application; import com.codenames.codenames.backend.chat.application.ChatService; +import com.codenames.codenames.backend.database.repository.LobbyRepository; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; @@ -30,6 +31,7 @@ public class LobbyService { private final LobbyCodeGenerator generator; private final GameService gameService; private final ChatService chatService; + private final LobbyRepository lobbyRepository; /** * Creates a new {@code LobbyService}. @@ -37,10 +39,11 @@ public class LobbyService { * @param generator the lobby code generator used to create unique lobby codes */ public LobbyService( - LobbyCodeGenerator generator, ChatService chatService, GameService gameService) { + LobbyCodeGenerator generator, ChatService chatService, GameService gameService, LobbyRepository lobbyRepository) { this.generator = generator; this.chatService = chatService; this.gameService = gameService; + this.lobbyRepository = lobbyRepository; } /** @@ -158,6 +161,7 @@ public void checkLobbyStillHasPlayers(String lobbyCode) { lobbyList.remove(lobbyCode); chatService.clearLobbyHistory(lobbyCode); gameService.removeGame(lobbyCode); + lobbyRepository.deleteById(lobbyCode); log.info("{}: Lobby is empty, was removed from list.", lobbyCode); } } From aeab391bb953c71f176a73498927c8911492c432 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 17:57:40 +0200 Subject: [PATCH 122/172] fix: update test to include LobbyRepository as constructor argument --- .../backend/lobby/application/LobbyService.java | 8 +++++--- .../backend/lobby/application/LobbyServiceTest.java | 5 ++++- .../application/SystemStateRecoveryServiceTest.java | 12 +++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index fa994a1e..1bcef9fc 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -26,8 +26,7 @@ @Service public class LobbyService { - @Getter - private final Map lobbyList = new ConcurrentHashMap<>(); + @Getter private final Map lobbyList = new ConcurrentHashMap<>(); private final LobbyCodeGenerator generator; private final GameService gameService; private final ChatService chatService; @@ -39,7 +38,10 @@ public class LobbyService { * @param generator the lobby code generator used to create unique lobby codes */ public LobbyService( - LobbyCodeGenerator generator, ChatService chatService, GameService gameService, LobbyRepository lobbyRepository) { + LobbyCodeGenerator generator, + ChatService chatService, + GameService gameService, + LobbyRepository lobbyRepository) { this.generator = generator; this.chatService = chatService; this.gameService = gameService; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index 8c187132..aaa6bc55 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.when; import com.codenames.codenames.backend.chat.application.ChatService; +import com.codenames.codenames.backend.database.repository.LobbyRepository; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; @@ -37,14 +38,16 @@ class LobbyServiceTest { private LobbyService lobbyService; private LobbyCodeGenerator generator; private GameService gameService; + private LobbyRepository lobbyRepository; @BeforeEach void setup() { generator = mock(LobbyCodeGenerator.class); gameService = mock(GameService.class); ChatService chatService = mock(ChatService.class); + lobbyRepository = mock(LobbyRepository.class); - lobbyService = new LobbyService(generator, chatService, gameService); + lobbyService = new LobbyService(generator, chatService, gameService, lobbyRepository); when(generator.generateLobbyCode()).thenReturn("ABCDE"); } diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java index 1aabc3ca..f17b3cf6 100644 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java @@ -4,8 +4,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; import com.codenames.codenames.backend.chat.application.ChatService; +import com.codenames.codenames.backend.database.repository.LobbyRepository; import com.codenames.codenames.backend.game.api.dto.CardDto; import com.codenames.codenames.backend.game.api.dto.ClueDto; import com.codenames.codenames.backend.game.api.dto.GameStateDto; @@ -29,13 +31,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; class SystemStateRecoveryServiceTest { + private LobbyRepository lobbyRepository; @TempDir Path tempDir; + @BeforeEach + void setUp() { + lobbyRepository = mock(LobbyRepository.class); + } + @Test void recoverOnStartupDoesNothingWhenNoSnapshotExists() { TestContext context = createContext(tempDir.resolve("state.json")); @@ -242,7 +251,8 @@ private TestContext createContext(Path stateFile) { new CardGenerator("CodenamesWordlist.txt"), new ClueValidationService()); GameService gameService = new GameService(gameManagerFactory, new DataTransferObjectService()); LobbyService lobbyService = - new LobbyService(new LobbyCodeGenerator(), new ChatService(null), gameService); + new LobbyService( + new LobbyCodeGenerator(), new ChatService(null), gameService, lobbyRepository); SystemStateRecoveryService recoveryService = new SystemStateRecoveryService(stateStore, lobbyService, gameService, gameManagerFactory); From c17c5cfe26937fb19375e79d1c66f434700280bd Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 18:07:45 +0200 Subject: [PATCH 123/172] test: add test to verify deletion --- .../codenames/backend/lobby/application/LobbyServiceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index aaa6bc55..f6d943b1 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -266,6 +266,7 @@ void testLobbyIsRemovedWhenItIsEmpty() { lobbyService.checkLobbyStillHasPlayers("ABCDE"); assertFalse(lobbyService.getLobbyList().containsKey("ABCDE")); verify(gameService, times(1)).removeGame("ABCDE"); + verify(lobbyRepository, times(1)).deleteById("ABCDE"); } @Test From 703c4f3c31104df513c5571c1dba3bf37de63884 Mon Sep 17 00:00:00 2001 From: XtophB Date: Thu, 11 Jun 2026 19:42:48 +0200 Subject: [PATCH 124/172] fix: to avoid uniqueness violation we try deleting and inserting --- .../backend/database/persistence/PersistenceService.java | 2 ++ .../database/persistence/PersistenceServiceTest.java | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java index a24cc438..48fd3177 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceService.java @@ -49,6 +49,8 @@ public void saveSnapShot(String lobbyCode) { List playerList = lobbyService.getPlayersDto(lobbyCode); LobbyEntity lobbySnapShot = persistenceMapper.mapAggregateParentLobbyEntity(lobbyCode, gameManager, playerList); + lobbyRepository.deleteById(lobbyCode); + lobbyRepository.flush(); lobbyRepository.save(lobbySnapShot); log.info("Snapshot has been saved to DB"); } diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index c91ae761..d8a7708e 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -163,4 +163,11 @@ void testSaveSnapshot_card() { assertEquals("RED", cardList.get(24).getColor()); assertFalse(cardList.get(24).getIsGuessed()); } + + @Test + void testSaveSnapshot_noUniquenessViolationOnSecondSave() { + persistenceService.saveSnapShot(lobbyCode); + persistenceService.saveSnapShot(lobbyCode); + assertEquals(lobbyCode, lobbyRepository.findById(lobbyCode).get().getLobbyCode()); + } } From e8e2892ba576555bc6327b6da7624d8b9794743e Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 12 Jun 2026 12:53:00 +0200 Subject: [PATCH 125/172] refactor: delete Serialization class (dead code) --- .../serialization/SerializationJson.java | 36 ---------- .../serialization/SerializationJsonTest.java | 71 ------------------- 2 files changed, 107 deletions(-) delete mode 100644 src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java delete mode 100644 src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java diff --git a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java b/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java deleted file mode 100644 index 01c8fc73..00000000 --- a/src/main/java/com/codenames/codenames/backend/serialization/SerializationJson.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.codenames.codenames.backend.serialization; - -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.stereotype.Component; - -/** Service to serialize the game state DTO into JSON, which is then sent to the frontend. */ -@Component -public class SerializationJson { - private final ObjectMapper mapper; - - /** - * Constructor for serialization (mapper passed as argument to enable mocking in tests). - * - * @param mapper the ObjectMapper object that is used to serialize into JSON - */ - public SerializationJson(ObjectMapper mapper) { - this.mapper = mapper; - } - - /** - * Method to serialize the game state DTO into JSON. - * - * @param gameStateDataTransferObject the DTO of the game state - * @return the string JSON containing: winner, points, list of cards and their information - * @throws IllegalStateException if serialization fails - */ - public String serialize(GameStateDto gameStateDataTransferObject) { - try { - return mapper.writeValueAsString(gameStateDataTransferObject); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Error while parsing into JSON.", e); - } - } -} diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java deleted file mode 100644 index 4282b0de..00000000 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.codenames.codenames.backend.serialization; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.codenames.codenames.backend.game.api.dto.CardDto; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.game.domain.Color; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class SerializationJsonTest { - - SerializationJson serializer; - Card card; - List dummyList; - GameStateDto dummyGameState; - ObjectMapper mapper = new ObjectMapper(); - private static final Team redTeam = Team.RED; - private static final Role spymaster = Role.SPYMASTER; - - @BeforeEach - void setUp() { - card = new Card("TEST", Color.RED); - serializer = new SerializationJson(mapper); - - dummyList = List.of(new CardDto("TEST", null, false)); - dummyGameState = - new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), 2, dummyList); - } - - @Test - void testSerialize_pass() { - String expectedResult = - "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," - + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," - + "\"guessAmount\":1},\"remainingGuesses\":2,\"cardList\":[{\"word\":\"TEST\"," - + "\"color\":null,\"isGuessed\":false}]}"; - String result = serializer.serialize(dummyGameState); - assertEquals(expectedResult, result); - } - - // writeValueAsString throws JsonProcessingException, - // I either need try catch block or have my method throw the exception. - @Test - void testSerialize_exception() throws JsonProcessingException { - ObjectMapper mockMapper = mock(ObjectMapper.class); - SerializationJson exceptionSerializer = new SerializationJson(mockMapper); - var exception = new MockJsonProcessingException("Because of protected constructors"); - when(mockMapper.writeValueAsString(any())).thenThrow(exception); - assertThrows(IllegalStateException.class, () -> exceptionSerializer.serialize(null)); - } - - // Testing exception for writeValueAsString requires passing objectMapper in a constructor. - // We then create this custom exception and throw it. (Solution found on stack overflow) - static class MockJsonProcessingException extends JsonProcessingException { - public MockJsonProcessingException(String message) { - super(message); - } - } -} From 3491cbe74526cd6a46c6b2c790ee4fe0078bfbe8 Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 12 Jun 2026 12:54:39 +0200 Subject: [PATCH 126/172] refactor: delete json persistency (redundant due to postgresql DB) --- .../SystemStatePersistenceService.java | 56 ---- .../SystemStateRecoveryService.java | 146 ---------- .../domain/snapshot/SystemSnapshot.java | 21 -- .../infrastructure/JsonStateStore.java | 99 ------- .../SystemStatePersistenceServiceTest.java | 50 ---- .../SystemStateRecoveryServiceTest.java | 268 ------------------ .../infrastructure/JsonStateStoreTest.java | 173 ----------- 7 files changed, 813 deletions(-) delete mode 100644 src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java delete mode 100644 src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java delete mode 100644 src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java delete mode 100644 src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java delete mode 100644 src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java delete mode 100644 src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java delete mode 100644 src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java deleted file mode 100644 index 04b017b5..00000000 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceService.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.codenames.codenames.backend.recovery.application; - -import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** - * Persists the current runtime state of the backend. - * - *

Collects lobby and game snapshots and stores them through the configured {@link - * JsonStateStore} to enable recovery after application restart. - */ -@Slf4j -@Service -public class SystemStatePersistenceService { - - private final JsonStateStore stateStore; - private final LobbyService lobbyService; - private final GameService gameService; - - /** - * Creates a persistence service responsible for storing backend state snapshots. - * - * @param stateStore JSON storage implementation - * @param lobbyService service providing lobby snapshots - * @param gameService service providing game snapshots - */ - public SystemStatePersistenceService( - JsonStateStore stateStore, LobbyService lobbyService, GameService gameService) { - - this.stateStore = stateStore; - this.lobbyService = lobbyService; - this.gameService = gameService; - } - - /** - * Persists the current backend state. - * - *

Creates a {@link SystemSnapshot} containing all active lobbies and games and stores it using - * the configured {@link JsonStateStore}. - */ - public void persistCurrentState() { - - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - lobbyService.getLobbySnapshots(), - gameService.getGameSnapshots()); - - stateStore.save(snapshot); - log.info("Saving a new system snapshot."); - } -} diff --git a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java b/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java deleted file mode 100644 index 89afbdf9..00000000 --- a/src/main/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryService.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.codenames.codenames.backend.recovery.application; - -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.game.domain.GameManager; -import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.lobby.domain.Lobby; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** Restores persisted lobbies and games into in-memory runtime services on backend startup. */ -@Slf4j -@Service -public class SystemStateRecoveryService { - - private final JsonStateStore stateStore; - private final LobbyService lobbyService; - private final GameService gameService; - private final GameManagerFactory gameManagerFactory; - - /** - * Creates a recovery service. - * - * @param stateStore JSON state store used for loading snapshots - * @param lobbyService lobby runtime service - * @param gameService game runtime service - * @param gameManagerFactory factory used to rebuild game managers from snapshots - */ - public SystemStateRecoveryService( - JsonStateStore stateStore, - LobbyService lobbyService, - GameService gameService, - GameManagerFactory gameManagerFactory) { - this.stateStore = stateStore; - this.lobbyService = lobbyService; - this.gameService = gameService; - this.gameManagerFactory = gameManagerFactory; - } - - /** Loads and restores persisted state at startup when a compatible snapshot exists. */ - @jakarta.annotation.PostConstruct - public void recoverOnStartup() { - stateStore - .load() - .ifPresent( - snapshot -> { - if (snapshot.schemaVersion() != SystemSnapshot.CURRENT_SCHEMA_VERSION) { - log.warn( - "Skipping snapshot restore due to schema mismatch. Found {}, expected {}.", - snapshot.schemaVersion(), - SystemSnapshot.CURRENT_SCHEMA_VERSION); - return; - } - restoreLobbies(snapshot.lobbies()); - restoreGames(snapshot.games()); - int lobbyCount = (snapshot.lobbies() == null) ? 0 : snapshot.lobbies().size(); - int gameCount = (snapshot.games() == null) ? 0 : snapshot.games().size(); - log.info("Restoring of {} lobbies and {} games successful.", - lobbyCount, gameCount - ); - }); - } - - /** - * Restores all lobby snapshots into {@link LobbyService}. - * - * @param lobbySnapshots persisted lobby player lists keyed by lobby code - */ - private void restoreLobbies(Map> lobbySnapshots) { - if (lobbySnapshots == null || lobbySnapshots.isEmpty()) { - log.info("There were no lobbies to restore."); - return; - } - for (Map.Entry> entry : lobbySnapshots.entrySet()) { - Lobby restoredLobby = buildLobby(entry.getKey(), entry.getValue()); - if (restoredLobby != null) { - lobbyService.restoreLobby(entry.getKey(), restoredLobby); - } - } - } - - /** - * Restores all game snapshots into {@link GameService}. - * - * @param gameSnapshots persisted game states keyed by lobby code - */ - private void restoreGames(Map gameSnapshots) { - if (gameSnapshots == null || gameSnapshots.isEmpty()) { - log.info("There were no games to restore."); - return; - } - for (Map.Entry entry : gameSnapshots.entrySet()) { - GameManager restoredGame = gameManagerFactory.createFromSnapshot(entry.getValue()); - gameService.restoreGameManager(entry.getKey(), restoredGame); - } - } - - /** - * Builds a runtime lobby from a persisted lobby snapshot. - * - * @param lobbyCode target lobby code - * @param players persisted lobby players - * @return rebuilt lobby, or {@code null} when player data is invalid - */ - private Lobby buildLobby(String lobbyCode, List players) { - if (players == null || players.isEmpty()) { - log.warn("Skipping restore for lobby {} due to missing player data.", lobbyCode); - return null; - } - - List validPlayers = - players.stream() - .filter(player -> player.username() != null && !player.username().isBlank()) - .sorted(Comparator.comparing(PlayerDto::isHost).reversed()) - .toList(); - - if (validPlayers.isEmpty()) { - log.warn("Skipping restore for lobby {} due to invalid player data.", lobbyCode); - return null; - } - - PlayerDto host = validPlayers.get(0); - Lobby lobby = new Lobby(lobbyCode, host.username()); - - for (PlayerDto player : validPlayers) { - if (!player.username().equals(host.username())) { - lobby.addPlayer(player.username(), player.isHost()); - } - if (player.team() != null) { - lobby.setPlayerTeam(player.username(), player.team()); - } - if (player.role() != null) { - lobby.setPlayerRole(player.username(), player.role()); - } - } - - return lobby; - } -} diff --git a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java b/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java deleted file mode 100644 index 835db7f4..00000000 --- a/src/main/java/com/codenames/codenames/backend/recovery/domain/snapshot/SystemSnapshot.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.codenames.codenames.backend.recovery.domain.snapshot; - -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import java.util.List; -import java.util.Map; - -/** - * Root snapshot aggregate for persisted backend runtime state. - * - * @param schemaVersion persisted schema version - * @param lobbies lobby player lists keyed by lobby code - * @param games game states keyed by lobby code - */ -public record SystemSnapshot( - int schemaVersion, - Map> lobbies, - Map games) { - - public static final int CURRENT_SCHEMA_VERSION = 2; -} diff --git a/src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java b/src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java deleted file mode 100644 index 2975d543..00000000 --- a/src/main/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStore.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.codenames.codenames.backend.recovery.infrastructure; - -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.nio.file.AtomicMoveNotSupportedException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Optional; -import java.util.concurrent.locks.ReentrantLock; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -/** - * JSON-backed storage for persisted system snapshots. - * - *

Writes are synchronized and performed via temporary-file replace to reduce corruption risk. - */ -@Component -@Getter -public class JsonStateStore { - - private final ObjectMapper objectMapper; - private final Path stateFilePath; - private final ReentrantLock ioLock = new ReentrantLock(); - - /** - * Creates a new state store with configured target file path. - * - * @param objectMapper mapper used for JSON serialization - * @param stateFile configured path to the persisted state file - */ - public JsonStateStore( - ObjectMapper objectMapper, @Value("${app.state-file:data/state.json}") String stateFile) { - this.objectMapper = objectMapper; - this.stateFilePath = Path.of(stateFile); - } - - /** - * Persists the full system snapshot atomically. - * - * @param snapshot full snapshot to store - */ - public void save(SystemSnapshot snapshot) { - ioLock.lock(); - try { - Path parentDirectory = stateFilePath.getParent(); - if (parentDirectory != null) { - Files.createDirectories(parentDirectory); - } - - Path tempFilePath = stateFilePath.resolveSibling(stateFilePath.getFileName() + ".tmp"); - objectMapper.writerWithDefaultPrettyPrinter().writeValue(tempFilePath.toFile(), snapshot); - - moveAtomically(tempFilePath, stateFilePath); - } catch (IOException exception) { - throw new IllegalStateException("Failed to persist state snapshot.", exception); - } finally { - ioLock.unlock(); - } - } - - /** - * Loads the persisted system snapshot when present. - * - * @return optional snapshot - */ - public Optional load() { - ioLock.lock(); - try { - if (!Files.exists(stateFilePath) || Files.size(stateFilePath) == 0L) { - return Optional.empty(); - } - return Optional.of(objectMapper.readValue(stateFilePath.toFile(), SystemSnapshot.class)); - } catch (IOException exception) { - throw new IllegalStateException("Failed to load persisted state snapshot.", exception); - } finally { - ioLock.unlock(); - } - } - - /** - * Replaces target file with source file using atomic move where supported. - * - * @param source temporary source file - * @param target final target file - * @throws IOException when move fails - */ - private void moveAtomically(Path source, Path target) throws IOException { - try { - Files.move( - source, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - } catch (AtomicMoveNotSupportedException exception) { - Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); - } - } -} diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java deleted file mode 100644 index 15efbf45..00000000 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStatePersistenceServiceTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.codenames.codenames.backend.recovery.application; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -class SystemStatePersistenceServiceTest { - - @Test - void persistCurrentStateSavesCurrentSystemSnapshot() { - JsonStateStore stateStore = mock(JsonStateStore.class); - LobbyService lobbyService = mock(LobbyService.class); - GameService gameService = mock(GameService.class); - - Map> lobbySnapshots = Map.of("ABCDE", List.of()); - - Map gameSnapshots = Map.of(); - - when(lobbyService.getLobbySnapshots()).thenReturn(lobbySnapshots); - when(gameService.getGameSnapshots()).thenReturn(gameSnapshots); - - SystemStatePersistenceService persistenceService = - new SystemStatePersistenceService(stateStore, lobbyService, gameService); - - persistenceService.persistCurrentState(); - - ArgumentCaptor snapshotCaptor = ArgumentCaptor.forClass(SystemSnapshot.class); - - verify(stateStore, times(1)).save(snapshotCaptor.capture()); - - SystemSnapshot snapshot = snapshotCaptor.getValue(); - - assertEquals(SystemSnapshot.CURRENT_SCHEMA_VERSION, snapshot.schemaVersion()); - assertEquals(lobbySnapshots, snapshot.lobbies()); - assertEquals(gameSnapshots, snapshot.games()); - } -} diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java deleted file mode 100644 index ca6c017c..00000000 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.codenames.codenames.backend.recovery.application; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; - -import com.codenames.codenames.backend.chat.application.ChatService; -import com.codenames.codenames.backend.database.repository.LobbyRepository; -import com.codenames.codenames.backend.game.api.dto.CardDto; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.game.domain.Color; -import com.codenames.codenames.backend.game.domain.GameManager; -import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.lobby.domain.Lobby; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class SystemStateRecoveryServiceTest { - - private LobbyRepository lobbyRepository; - @TempDir Path tempDir; - - @BeforeEach - void setUp() { - lobbyRepository = mock(LobbyRepository.class); - } - - @Test - void recoverOnStartupDoesNothingWhenNoSnapshotExists() { - TestContext context = createContext(tempDir.resolve("state.json")); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupSkipsSnapshotWhenSchemaVersionDiffers() { - TestContext context = createContext(tempDir.resolve("state.json")); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION + 1, Map.of(), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { - TestContext context = createContext(tempDir.resolve("state.json")); - List lobbyPlayers = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - GameStateDto gameSnapshot = - new GameStateDto( - null, - Team.RED, - Role.OPERATIVE, - new ClueDto("ANIMAL", 2), - 3, - List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false))); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", lobbyPlayers), - Map.of("ABCDE", gameSnapshot)); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - Lobby restoredLobby = context.lobbyService().getLobbyList().get("ABCDE"); - assertEquals(2, restoredLobby.getPlayerList().size()); - assertEquals(Team.RED, restoredLobby.getPlayerTeam("Host")); - assertEquals(Role.SPYMASTER, restoredLobby.getPlayerRole("Host")); - assertEquals(Team.BLUE, restoredLobby.getPlayerTeam("Player")); - assertEquals(Role.OPERATIVE, restoredLobby.getPlayerRole("Player")); - - assertTrue(context.gameService().isGameStarted("ABCDE")); - GameManager restoredGame = context.gameService().getGameState("ABCDE"); - assertEquals(Team.RED, restoredGame.getCurrentTurn()); - assertEquals(Role.OPERATIVE, restoredGame.getCurrentPhase()); - assertEquals("ANIMAL", restoredGame.getCurrentClue().word()); - assertTrue(restoredGame.getCardList().get(0).isGuessed()); - assertFalse(restoredGame.getCardList().get(1).isGuessed()); - } - - @Test - void recoverOnStartupWithCurrentSchemaAndEmptyMapsDoesNothing() { - TestContext context = createContext(tempDir.resolve("state-empty.json")); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupHandlesNullLobbyAndGameMaps() { - TestContext context = createContext(tempDir.resolve("state-null-maps.json")); - SystemSnapshot snapshot = new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, null, null); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupSkipsLobbyWhenSnapshotEntryIsNull() { - TestContext context = createContext(tempDir.resolve("state-null-lobby-entry.json")); - Map> lobbies = new HashMap<>(); - lobbies.put("ABCDE", null); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, lobbies, Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenPlayersListIsNull() { - TestContext context = createContext(tempDir.resolve("state-null-players.json")); - Map> lobbies = new HashMap<>(); - lobbies.put("ABCDE", null); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, lobbies, Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenPlayersListIsEmpty() { - TestContext context = createContext(tempDir.resolve("state-empty-players.json")); - List lobbySnapshot = List.of(); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenAllUsernamesAreInvalid() { - TestContext context = createContext(tempDir.resolve("state-invalid-usernames.json")); - List lobbySnapshot = - List.of( - new PlayerDto(" ", Team.RED, Role.SPYMASTER, true), - new PlayerDto(null, Team.BLUE, Role.OPERATIVE, false)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupRestoresLobbyWhenSomeTeamOrRoleValuesAreMissing() { - TestContext context = createContext(tempDir.resolve("state-missing-team-role.json")); - List lobbySnapshot = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", null, null, false)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - Lobby restoredLobby = context.lobbyService().getLobbyList().get("ABCDE"); - assertEquals(Team.RED, restoredLobby.getPlayerTeam("Host")); - assertEquals(Role.SPYMASTER, restoredLobby.getPlayerRole("Host")); - assertNull(restoredLobby.getPlayerTeam("Player")); - assertNull(restoredLobby.getPlayerRole("Player")); - } - - @Test - void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { - TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); - GameStateDto gameSnapshot = - new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, 0, List.of(new CardDto("Tree", Color.BLUE, false))); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertTrue(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupRestoresOnlyLobbiesWhenGamesMapIsNull() { - TestContext context = createContext(tempDir.resolve("state-games-null-lobbies-present.json")); - List lobbySnapshot = List.of(new PlayerDto("Host", Team.RED, Role.SPYMASTER, true)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), null); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().containsKey("ABCDE")); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - private TestContext createContext(Path stateFile) { - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - GameManagerFactory gameManagerFactory = - new GameManagerFactory( - new CardGenerator("CodenamesWordlist.txt"), new ClueValidationService()); - GameService gameService = new GameService(gameManagerFactory, new DataTransferObjectService()); - LobbyService lobbyService = - new LobbyService( - new LobbyCodeGenerator(), new ChatService(null), gameService, lobbyRepository); - SystemStateRecoveryService recoveryService = - new SystemStateRecoveryService(stateStore, lobbyService, gameService, gameManagerFactory); - - return new TestContext(stateStore, lobbyService, gameService, recoveryService); - } - - private record TestContext( - JsonStateStore stateStore, - LobbyService lobbyService, - GameService gameService, - SystemStateRecoveryService recoveryService) {} -} diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java deleted file mode 100644 index dddeac0a..00000000 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.codenames.codenames.backend.recovery.infrastructure; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class JsonStateStoreTest { - - @TempDir Path tempDir; - - @Test - void loadReturnsEmptyWhenStateFileDoesNotExist() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isEmpty()); - } - - @Test - void loadReturnsEmptyWhenStateFileIsEmpty() throws IOException { - Path stateFile = tempDir.resolve("state.json"); - Files.createFile(stateFile); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isEmpty()); - } - - @Test - void saveAndLoadRoundTrip() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - List lobbyPlayers = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - - GameStateDto gameSnapshot = - new GameStateDto( - null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), 3, List.of()); - - SystemSnapshot expectedSnapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", lobbyPlayers), - Map.of("ABCDE", gameSnapshot)); - - stateStore.save(expectedSnapshot); - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isPresent()); - assertTrue(Files.exists(stateFile)); - - SystemSnapshot actualSnapshot = loadedSnapshot.get(); - assertEquals(SystemSnapshot.CURRENT_SCHEMA_VERSION, actualSnapshot.schemaVersion()); - assertEquals(1, actualSnapshot.lobbies().size()); - assertEquals(1, actualSnapshot.games().size()); - - List actualPlayers = actualSnapshot.lobbies().get("ABCDE"); - assertEquals(2, actualPlayers.size()); - assertEquals("Host", actualPlayers.get(0).username()); - assertEquals(Team.RED, actualPlayers.get(0).team()); - assertEquals(Role.SPYMASTER, actualPlayers.get(0).role()); - assertTrue(actualPlayers.get(0).isHost()); - - GameStateDto actualGameSnapshot = actualSnapshot.games().get("ABCDE"); - assertEquals(Team.RED, actualGameSnapshot.currentTurn()); - assertEquals(Role.OPERATIVE, actualGameSnapshot.currentPhase()); - assertEquals("ANIMAL", actualGameSnapshot.currentClue().word()); - assertEquals(2, actualGameSnapshot.currentClue().guessAmount()); - assertTrue(actualGameSnapshot.cardList().isEmpty()); - } - - @Test - void saveOverwritesPreviousSnapshot() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - SystemSnapshot firstSnapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - SystemSnapshot secondSnapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", List.of()), - Map.of()); - - stateStore.save(firstSnapshot); - stateStore.save(secondSnapshot); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isPresent()); - assertTrue(loadedSnapshot.get().lobbies().containsKey("ABCDE")); - assertEquals(1, loadedSnapshot.get().lobbies().size()); - } - - @Test - void saveThrowsIllegalStateWhenStateParentCannotBeCreated() throws IOException { - Path fileAsParent = tempDir.resolve("not-a-directory"); - Files.writeString(fileAsParent, "occupied"); - Path stateFile = fileAsParent.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - - assertThrows(IllegalStateException.class, () -> stateStore.save(snapshot)); - } - - @Test - void loadThrowsIllegalStateWhenJsonIsInvalid() throws IOException { - Path stateFile = tempDir.resolve("state.json"); - Files.writeString(stateFile, "{invalid-json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - assertThrows(IllegalStateException.class, stateStore::load); - } - - @Test - void gettersExposeConfiguredDependencies() { - ObjectMapper mapper = new ObjectMapper(); - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(mapper, stateFile.toString()); - - assertSame(mapper, stateStore.getObjectMapper()); - assertEquals(stateFile, stateStore.getStateFilePath()); - assertNotNull(stateStore.getIoLock()); - } - - @Test - void saveWorksWhenStateFileHasNoParentDirectory() throws IOException { - String fileName = "json-state-store-" + System.nanoTime() + ".json"; - Path stateFile = Path.of(fileName); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), fileName); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - - try { - stateStore.save(snapshot); - - assertTrue(Files.exists(stateFile)); - assertTrue(stateStore.load().isPresent()); - } finally { - Files.deleteIfExists(stateFile); - Files.deleteIfExists(Path.of(fileName + ".tmp")); - } - } - - private Optional loadSnapshot(JsonStateStore stateStore) { - return stateStore.load(); - } -} From e86aa31f734c2f3e30441804ed5f7d463c4f1e05 Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 12 Jun 2026 12:58:29 +0200 Subject: [PATCH 127/172] refactor: remove usages of now nonexistent json persistence calls --- .../backend/lobby/api/LobbyController.java | 11 +---------- .../backend/lobby/api/LobbyControllerTest.java | 15 --------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 03aa213a..6c579251 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -3,7 +3,6 @@ import com.codenames.codenames.backend.lobby.api.dto.LobbyResponse; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -25,18 +24,15 @@ public class LobbyController { private final LobbyService service; - private final SystemStatePersistenceService persistenceService; private static final String LOBBY_NOT_FOUND = "Could not find lobby."; /** * Creates a new {@code LobbyController}. * * @param service the lobby service used to handle business logic - * @param persistenceService service used to persist current backend state */ - public LobbyController(LobbyService service, SystemStatePersistenceService persistenceService) { + public LobbyController(LobbyService service) { this.service = service; - this.persistenceService = persistenceService; } /** @@ -97,10 +93,6 @@ public ResponseEntity leaveLobby( if (left) { service.checkLobbyStillHasPlayers(lobbyCode); - if (wasStarted) { - persistenceService.persistCurrentState(); - } - return ResponseEntity.ok( new LobbyResponse( "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false)); @@ -170,7 +162,6 @@ public ResponseEntity startGame( boolean isStarted = service.startGame(lobbyCode, username); if (isStarted) { - persistenceService.persistCurrentState(); return ResponseEntity.ok( new LobbyResponse( "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true)); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index a58c68ed..1be43b69 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -1,7 +1,5 @@ package com.codenames.codenames.backend.lobby.api; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -12,7 +10,6 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.application.SystemStatePersistenceService; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,8 +25,6 @@ class LobbyControllerTest { @MockBean private LobbyService service; - @MockBean private SystemStatePersistenceService persistenceService; - @Test void createLobbyShouldReturn200() throws Exception { when(service.createLobby("TestUser")).thenReturn("ABCDE"); @@ -39,8 +34,6 @@ void createLobbyShouldReturn200() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Successfully created Lobby.")) .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); - - verifyNoInteractions(persistenceService); } @Test @@ -73,8 +66,6 @@ void joinLobbyShouldReturn200WhenSuccess() throws Exception { .perform(get("/lobby/ABCDE/join").param("username", "TestUser")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); - - verifyNoInteractions(persistenceService); } @Test @@ -104,8 +95,6 @@ void leaveLobbyShouldReturn200WhenSuccess() throws Exception { .perform(get("/lobby/ABCDE/leave").param("username", "TestUser")) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Left lobby successfully.")); - - verifyNoInteractions(persistenceService); } @Test @@ -139,8 +128,6 @@ void selectPositionShouldReturn200whenSuccess() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Position selected successfully.")) .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); - - verifyNoInteractions(persistenceService); } @Test @@ -196,8 +183,6 @@ void testStartGameReturns200WhenConditionIsMet() throws Exception { .andExpect(jsonPath("$.lobbyCode").value("ABCDE")) .andExpect(jsonPath("$.playerList[0].username").value("Alice")) .andExpect(jsonPath("$.isStarted").value("true")); - - verify(persistenceService).persistCurrentState(); } @Test From cb266a86a55d0c27d6a18a83c560341e9c1be3f5 Mon Sep 17 00:00:00 2001 From: XtophB Date: Fri, 12 Jun 2026 13:06:37 +0200 Subject: [PATCH 128/172] refactor: remove volume as it was only used by the json persistence --- docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c3c49d10..4e8780f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,8 +19,6 @@ services: restart: unless-stopped ports: - "53213:8080" - volumes: - - ./data:/app/data environment: # Inject datasource url into application.yml when containerized SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/codenames SPRING_DATASOURCE_USERNAME: codenames_user From 23de257fb296642eecfeebbdcafa2c4ebbc6d8f7 Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 12:43:23 +0200 Subject: [PATCH 129/172] feat: prepare backend for player UUID authentication --- .../backend/database/entity/PlayerEntity.java | 2 ++ .../backend/lobby/api/LobbyController.java | 2 +- .../backend/lobby/api/LobbySocketController.java | 2 +- .../backend/lobby/api/dto/JoinMessage.java | 1 + .../backend/lobby/application/LobbyService.java | 4 ++-- .../codenames/backend/lobby/domain/Lobby.java | 14 +++++++++++--- .../codenames/backend/lobby/domain/Player.java | 2 +- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java index a8f319ea..d62d8eb0 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java @@ -35,4 +35,6 @@ public class PlayerEntity { private String team; @Column(length = 9, nullable = false) private String role; + @Column(nullable = false, unique = true) + private String uuid; } diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 6c579251..fca51504 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -65,7 +65,7 @@ public ResponseEntity createLobby(@RequestParam String username) @GetMapping("/{lobbyCode}/join") public ResponseEntity joinLobby( @RequestParam String username, @PathVariable String lobbyCode) { - boolean joined = service.joinLobby(username, lobbyCode); + boolean joined = service.joinLobby(username, lobbyCode, null); if (joined) { return ResponseEntity.ok( diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 7d4cc8dc..5fd9ab77 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -71,7 +71,7 @@ public void join(JoinMessage message, SimpMessageHeaderAccessor headerAccessor) log.error(e.getMessage()); } - boolean joined = lobbyService.joinLobby(message.getName(), message.getCode()); + boolean joined = lobbyService.joinLobby(message.getName(), message.getCode(), message.getUuid()); boolean reconnect = lobbyService.getPlayers(message.getCode()).stream() .anyMatch(player -> player.username().equals(message.getName())); diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java index 185f503b..e5f7bd23 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/JoinMessage.java @@ -13,4 +13,5 @@ public class JoinMessage { private String name; private String code; + private String uuid; } diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index 1bcef9fc..ee92cf0e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -85,11 +85,11 @@ private void addGameManagerForLobby(Lobby lobby, String lobbyCode) { * @param lobbyCode the lobby code identifying the lobby * @return {@code true} if the player successfully joined, {@code false} otherwise */ - public boolean joinLobby(String username, String lobbyCode) { + public boolean joinLobby(String username, String lobbyCode, String uuid) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { log.info("{}: a player has joined", lobbyCode); - return lobby.addPlayer(username); + return lobby.addPlayer(username, uuid); } log.error("{}: an error occurred when joining lobby", lobbyCode); return false; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 9f6f96cb..a7205bd2 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -55,7 +55,7 @@ public boolean addPlayer(String username, boolean isHost) { return false; } - playerList.add(new Player(username, isHost)); + playerList.add(new Player(username, isHost, null)); return true; } @@ -66,8 +66,16 @@ public boolean addPlayer(String username, boolean isHost) { * @param username the username of the player * @return {@code true} if the player was added, {@code false} otherwise */ - public boolean addPlayer(String username) { - return addPlayer(username, false); + public boolean addPlayer(String username, String uuid) { + boolean alreadyExists = + playerList.stream().anyMatch(p -> p.username().equals(username)); + + if (alreadyExists || playerList.size() >= MAX_PLAYERS) { + return false; + } + + playerList.add(new Player(username, false, uuid)); + return true; } /** diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java index a2697933..7febd494 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Player.java @@ -5,5 +5,5 @@ * *

A player is identified by a username and may be associated with a WebSocket session. */ -public record Player(String username, boolean isHost) { +public record Player(String username, boolean isHost, String uuid) { } From 8edc8f9bb21dc12db1ef5d5dd408641c44c754ad Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 13:52:01 +0200 Subject: [PATCH 130/172] feat: add player UUID generation in backend --- .../backend/lobby/api/LobbyController.java | 78 ++++++++++--------- .../lobby/api/LobbySocketController.java | 3 +- .../backend/lobby/api/dto/LobbyResponse.java | 3 +- .../lobby/application/LobbyService.java | 15 +++- .../codenames/backend/lobby/domain/Lobby.java | 21 ++--- .../lobby/api/LobbyControllerTest.java | 19 +++-- .../lobby/api/LobbySocketControllerTest.java | 21 ++--- .../lobby/application/LobbyServiceTest.java | 30 +++---- .../backend/lobby/domain/LobbyTest.java | 27 +++---- .../backend/lobby/domain/PlayerTest.java | 2 +- 10 files changed, 116 insertions(+), 103 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index fca51504..66d7b77f 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -3,6 +3,7 @@ import com.codenames.codenames.backend.lobby.api.dto.LobbyResponse; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -46,12 +47,16 @@ public ResponseEntity createLobby(@RequestParam String username) String lobbyCode = service.createLobby(username); if (lobbyCode == null || lobbyCode.isBlank()) { return ResponseEntity.internalServerError() - .body(new LobbyResponse("Error while creating lobby.", "", null, false)); + .body(new LobbyResponse("Error while creating lobby.", "", null, false, null)); // 5. Parameter null } else { - List players = service.getPlayersDto(lobbyCode); + + // UUID des Hosts abrufen und mitsenden + Player host = service.getPlayer(lobbyCode, username); + String hostUuid = (host != null) ? host.uuid() : null; + return ResponseEntity.ok( - new LobbyResponse("Successfully created Lobby.", lobbyCode, players, false)); + new LobbyResponse("Successfully created Lobby.", lobbyCode, players, false, hostUuid)); } } @@ -64,16 +69,18 @@ public ResponseEntity createLobby(@RequestParam String username) */ @GetMapping("/{lobbyCode}/join") public ResponseEntity joinLobby( - @RequestParam String username, @PathVariable String lobbyCode) { - boolean joined = service.joinLobby(username, lobbyCode, null); - if (joined) { + @RequestParam String username, @PathVariable String lobbyCode) { + // GEÄNDERT: Nimmt jetzt ein Player-Objekt zurück anstatt boolean + Player joinedPlayer = service.joinLobby(username, lobbyCode, null); + + if (joinedPlayer != null) { // GEÄNDERT: Prüft, ob das Objekt nicht null ist return ResponseEntity.ok( - new LobbyResponse( - "Joined Lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false)); + new LobbyResponse( + "Joined Lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, joinedPlayer.uuid())); // UUID an Frontend geschickt } else { return ResponseEntity.badRequest() - .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false)); + .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); // 5. Parameter null } } @@ -86,7 +93,7 @@ public ResponseEntity joinLobby( */ @GetMapping("/{lobbyCode}/leave") public ResponseEntity leaveLobby( - @PathVariable String lobbyCode, @RequestParam String username) { + @PathVariable String lobbyCode, @RequestParam String username) { boolean wasStarted = service.getIsStarted(lobbyCode); boolean left = service.leaveLobby(username, lobbyCode); @@ -94,11 +101,11 @@ public ResponseEntity leaveLobby( service.checkLobbyStillHasPlayers(lobbyCode); return ResponseEntity.ok( - new LobbyResponse( - "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false)); + new LobbyResponse( + "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); // 5. Parameter null } else { return ResponseEntity.badRequest() - .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false)); + .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); // 5. Parameter null } } @@ -114,9 +121,8 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode List players = service.getPlayersDto(lobbyCode); boolean isStarted = service.getIsStarted(lobbyCode); return ResponseEntity.ok( - new LobbyResponse("Lobby info retrieved successfully.", lobbyCode, players, isStarted)); + new LobbyResponse("Lobby info retrieved successfully.", lobbyCode, players, isStarted, null)); // 5. Parameter null } - /** * Handles a request to select a team and role for a player. * @@ -125,29 +131,27 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode */ @PostMapping("/{lobbyCode}/select-position") public ResponseEntity selectPosition( - @PathVariable String lobbyCode, @RequestBody PlayerDto request) { + @PathVariable String lobbyCode, @RequestBody PlayerDto request) { boolean updated = - service.selectPosition(request.username(), lobbyCode, request.team(), request.role()); + service.selectPosition(request.username(), lobbyCode, request.team(), request.role()); if (updated) { - return ResponseEntity.ok( - new LobbyResponse( - "Position selected successfully.", - lobbyCode, - service.getPlayersDto(lobbyCode), - false)); + new LobbyResponse( + "Position selected successfully.", + lobbyCode, + service.getPlayersDto(lobbyCode), + false, null)); // 5. Parameter null } else { return ResponseEntity.badRequest() - .body( - new LobbyResponse( - "Could not assign selected team/role.", - lobbyCode, - service.getPlayersDto(lobbyCode), - false)); + .body( + new LobbyResponse( + "Could not assign selected team/role.", + lobbyCode, + service.getPlayersDto(lobbyCode), + false, null)); // 5. Parameter null } } - /** * Endpoint for starting a game, this is the last http-request only the host can make. * @@ -158,17 +162,17 @@ public ResponseEntity selectPosition( */ @GetMapping("/{lobbyCode}/start-game") public ResponseEntity startGame( - @PathVariable String lobbyCode, @RequestParam String username) { + @PathVariable String lobbyCode, @RequestParam String username) { boolean isStarted = service.startGame(lobbyCode, username); if (isStarted) { return ResponseEntity.ok( - new LobbyResponse( - "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true)); + new LobbyResponse( + "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true, null)); // 5. Parameter null } return ResponseEntity.badRequest() - .body( - new LobbyResponse( - "Could not start the game.", lobbyCode, service.getPlayersDto(lobbyCode), false)); + .body( + new LobbyResponse( + "Could not start the game.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); // 5. Parameter null } -} +} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 5fd9ab77..81939b4b 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -71,7 +71,8 @@ public void join(JoinMessage message, SimpMessageHeaderAccessor headerAccessor) log.error(e.getMessage()); } - boolean joined = lobbyService.joinLobby(message.getName(), message.getCode(), message.getUuid()); + Player joinedPlayer = lobbyService.joinLobby(message.getName(), message.getCode(), message.getUuid()); + boolean joined = (joinedPlayer != null); boolean reconnect = lobbyService.getPlayers(message.getCode()).stream() .anyMatch(player -> player.username().equals(message.getName())); diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java index bf864a20..2368a4f4 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/LobbyResponse.java @@ -11,5 +11,6 @@ public record LobbyResponse( String message, String lobbyCode, List playerList, - boolean isStarted + boolean isStarted, + String uuid ) {} diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index ee92cf0e..3b9b6a5d 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -85,14 +85,25 @@ private void addGameManagerForLobby(Lobby lobby, String lobbyCode) { * @param lobbyCode the lobby code identifying the lobby * @return {@code true} if the player successfully joined, {@code false} otherwise */ - public boolean joinLobby(String username, String lobbyCode, String uuid) { + public Player joinLobby(String username, String lobbyCode, String uuid) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { log.info("{}: a player has joined", lobbyCode); return lobby.addPlayer(username, uuid); } log.error("{}: an error occurred when joining lobby", lobbyCode); - return false; + return null; + } + + public Player getPlayer(String lobbyCode, String username) { + Lobby lobby = lobbyList.get(lobbyCode); + if (lobby != null) { + return lobby.getPlayerList().stream() + .filter(p -> p.username().equals(username)) + .findFirst() + .orElse(null); + } + return null; } /** diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index a7205bd2..229e5caa 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.UUID; import lombok.Getter; /** @@ -48,15 +49,17 @@ public Lobby(String lobbyCode, String username) { * @param isHost whether the player is the host of the lobby * @return {@code true} if the player was added, {@code false} otherwise */ - public boolean addPlayer(String username, boolean isHost) { + public Player addPlayer(String username, boolean isHost) { boolean alreadyExists = playerList.stream().anyMatch(p -> p.username().equals(username)); if (alreadyExists || playerList.size() >= MAX_PLAYERS) { - return false; + return null; } + String newUuid = UUID.randomUUID().toString(); + Player newPlayer = new Player(username, isHost, newUuid); playerList.add(new Player(username, isHost, null)); - return true; + return newPlayer; } /** @@ -66,16 +69,8 @@ public boolean addPlayer(String username, boolean isHost) { * @param username the username of the player * @return {@code true} if the player was added, {@code false} otherwise */ - public boolean addPlayer(String username, String uuid) { - boolean alreadyExists = - playerList.stream().anyMatch(p -> p.username().equals(username)); - - if (alreadyExists || playerList.size() >= MAX_PLAYERS) { - return false; - } - - playerList.add(new Player(username, false, uuid)); - return true; + public Player addPlayer(String username, String uuid) { + return addPlayer(username, false); } /** diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index 1be43b69..e20ef83a 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -10,6 +10,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,6 +29,7 @@ class LobbyControllerTest { @Test void createLobbyShouldReturn200() throws Exception { when(service.createLobby("TestUser")).thenReturn("ABCDE"); + when(service.getPlayer("ABCDE", "TestUser")).thenReturn(new Player("TestUser", true, "mock-uuid")); mockMvc .perform(get("/lobby/create").param("username", "TestUser")) @@ -60,12 +62,13 @@ void createLobbyNullLobbyCode() throws Exception { @Test void joinLobbyShouldReturn200WhenSuccess() throws Exception { - when(service.joinLobby("TestUser", "ABCDE")).thenReturn(true); + when(service.joinLobby("TestUser", "ABCDE", null)) + .thenReturn(new Player("TestUser", false, "mock-uuid-123")); mockMvc - .perform(get("/lobby/ABCDE/join").param("username", "TestUser")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); + .perform(get("/lobby/ABCDE/join").param("username", "TestUser")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); } @Test @@ -78,12 +81,12 @@ void getLobbyInfoShouldReturn200() throws Exception { @Test void joinLobbyShouldReturn400WhenNotFound() throws Exception { - when(service.joinLobby("TestUser", "XXXXX")).thenReturn(false); + when(service.joinLobby("TestUser", "XXXXX", null)).thenReturn(null); mockMvc - .perform(get("/lobby/XXXXX/join").param("username", "TestUser")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value("Could not find lobby.")); + .perform(get("/lobby/XXXXX/join").param("username", "TestUser")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Could not find lobby.")); } @Test diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index 058e7435..f2a85b52 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -51,6 +51,7 @@ void shouldRegisterJoinAndRegisterSession() { JoinMessage msg = new JoinMessage(); msg.setName("Max"); msg.setCode("ABCDE"); + msg.setUuid(null); SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); @@ -59,14 +60,14 @@ void shouldRegisterJoinAndRegisterSession() { accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE")).thenReturn(true); + when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn(new Player("Max", true, "mock-uuid")); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true))); + when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "mock-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); - verify(lobbyService).joinLobby("Max", "ABCDE"); + verify(lobbyService).joinLobby("Max", "ABCDE", null); assertEquals("Max", sessionRegistry.getUser("123")); assertEquals("ABCDE", sessionRegistry.getLobby("123")); @@ -88,9 +89,8 @@ void shouldSendErrorMessageWhenJoinFails() { attrs.put("sessionId", "123"); accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE")).thenReturn(false); + when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn(null); when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of()); - controller.join(msg, accessor); verify(messagingTemplate).convertAndSend("/topic/errors/123", "Join failed"); @@ -125,8 +125,8 @@ void shouldUseSessionAttributesFallbackWhenSessionIdIsNull() { attrs.put("sessionId", "123"); accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE")).thenReturn(true); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true))); + when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn(new Player("Max", true, "mock-uuid")); + when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "mock-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); @@ -134,7 +134,7 @@ void shouldUseSessionAttributesFallbackWhenSessionIdIsNull() { assertEquals("Max", sessionRegistry.getUser("123")); assertEquals("ABCDE", sessionRegistry.getLobby("123")); - verify(lobbyService).joinLobby("Max", "ABCDE"); + verify(lobbyService).joinLobby("Max", "ABCDE", null); verify(messagingTemplate).convertAndSend(eq("/topic/lobby/ABCDE"), any(Object.class)); verify(messagingTemplate).convertAndSend(eq("/topic/game/ABCDE"), any(Object.class)); } @@ -145,6 +145,7 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { JoinMessage msg = new JoinMessage(); msg.setName("Max"); msg.setCode("ABCDE"); + msg.setUuid("existing-uuid"); SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); @@ -152,8 +153,8 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { attrs.put("sessionId", "reconnect-1"); accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE")).thenReturn(false); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true))); + when(lobbyService.joinLobby("Max", "ABCDE", "existing-uuid")).thenReturn(null); + when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "existing-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index f6d943b1..6fb10336 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -54,9 +54,9 @@ void setup() { @Test void createLobbyReturnLobbyCode() { lobbyService.createLobby("Host"); - boolean result = lobbyService.joinLobby("TestUser", "ABCDE"); + Player result = lobbyService.joinLobby("TestUser", "ABCDE", null); - assertTrue(result); + assertNotNull(result); List players = lobbyService.getPlayers("ABCDE"); assertTrue(players.stream().anyMatch(p -> p.username().equals("TestUser"))); @@ -80,8 +80,8 @@ void createLobbyLobbyCodeIsBlank() { @Test void joinLobbyReturnFalseLobbyNotExists() { - boolean result = lobbyService.joinLobby("TestUser", "ABCDE"); - assertFalse(result); + Player result = lobbyService.joinLobby("TestUser", "ABCDE", null); + assertNull(result); } @Test @@ -141,7 +141,7 @@ void selectPositionShouldReturnFalseWhenPlayerIsNotInLobby() { @Test void selectPositionShouldReturnFalseWhenSecondSpymasterChoosesSameTeam() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE"); + lobbyService.joinLobby("P1", "ABCDE", null); boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.RED, Role.SPYMASTER); @@ -153,7 +153,7 @@ void selectPositionShouldReturnFalseWhenSecondSpymasterChoosesSameTeam() { @Test void selectPositionShouldReturnTrueWhenSpymastersChooseDifferentTeams() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE"); + lobbyService.joinLobby("P1", "ABCDE", null); boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.BLUE, Role.SPYMASTER); @@ -165,7 +165,7 @@ void selectPositionShouldReturnTrueWhenSpymastersChooseDifferentTeams() { @Test void selectPositionShouldReturnTrueWhenMultipleOperativesChooseSameTeam() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE"); + lobbyService.joinLobby("P1", "ABCDE", null); boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.OPERATIVE); boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.RED, Role.OPERATIVE); @@ -186,11 +186,11 @@ void getPlayersShouldReturnEmptyListWhenLobbyDoesNotExist() { void joinLobbyShouldReturnFalseWhenPlayerAlreadyExists() { lobbyService.createLobby("Host"); - boolean first = lobbyService.joinLobby("Max", "ABCDE"); - boolean second = lobbyService.joinLobby("Max", "ABCDE"); + Player first = lobbyService.joinLobby("Max", "ABCDE", null); + Player second = lobbyService.joinLobby("Max", "ABCDE", null); - assertTrue(first); - assertFalse(second); + assertNotNull(first); + assertNull(second); List players = lobbyService.getPlayers("ABCDE"); @@ -272,7 +272,7 @@ void testLobbyIsRemovedWhenItIsEmpty() { @Test void testLobbyIsNotRemovedWhenItHasPlayers() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("Player1", "ABCDE"); + lobbyService.joinLobby("Player1", "ABCDE", null); lobbyService.checkLobbyStillHasPlayers("ABCDE"); assertTrue(lobbyService.getLobbyList().containsKey("ABCDE")); } @@ -345,8 +345,8 @@ void testGetIsStartedGameServiceReturnsFalse() { @Test void testGetHostWorks() { lobbyService.createLobby("Alice"); - lobbyService.joinLobby("Bob", "ABCDE"); - lobbyService.joinLobby("Caesar", "ABCDE"); + lobbyService.joinLobby("Bob", "ABCDE", null); + lobbyService.joinLobby("Caesar", "ABCDE", null); String expected = "Alice"; String result = lobbyService.getHost("ABCDE"); @@ -376,7 +376,7 @@ void testRestoreLobbyAddsLobbyToLobbyList() { @Test void getLobbySnapshotsShouldReturnAllLobbyPlayerDtos() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("Player", "ABCDE"); + lobbyService.joinLobby("Player", "ABCDE", null); lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); Map> snapshots = lobbyService.getLobbySnapshots(); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index ed520e54..50d90a1b 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -1,12 +1,9 @@ package com.codenames.codenames.backend.lobby.domain; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + /** * Tests for {@link Lobby}. * @@ -27,7 +24,7 @@ void constructorShouldInitializeLobbyCorrectly() { void addPlayerShouldAddPlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1"); + lobby.addPlayer("P1", null); assertEquals(2, lobby.getPlayerList().size()); assertTrue(lobby.getPlayerList().stream().anyMatch(p -> p.username().equals("P1"))); @@ -37,10 +34,10 @@ void addPlayerShouldAddPlayer() { void addPlayerShouldNotExceedMaxPlayers() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1"); - lobby.addPlayer("P2"); - lobby.addPlayer("P3"); - lobby.addPlayer("P4"); + lobby.addPlayer("P1", null); + lobby.addPlayer("P2", null); + lobby.addPlayer("P3", null); + lobby.addPlayer("P4", null); assertEquals(4, lobby.getPlayerList().size()); } @@ -49,7 +46,7 @@ void addPlayerShouldNotExceedMaxPlayers() { void removePlayerShouldRemovePlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1"); + lobby.addPlayer("P1", null); lobby.removePlayer("P1"); assertFalse(lobby.getPlayerList().stream().anyMatch(p -> p.username().equals("P1"))); @@ -68,11 +65,11 @@ void removePlayerShouldDoNothingIfPlayerNotExists() { void addPlayerShouldNotAddDuplicatePlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); - boolean first = lobby.addPlayer("Max"); - boolean second = lobby.addPlayer("Max"); + Player first = lobby.addPlayer("Max", null); + Player second = lobby.addPlayer("Max", null); - assertTrue(first); - assertFalse(second); + assertNotNull(first); + assertNull(second); long count = lobby.getPlayerList().stream().filter(p -> p.username().equals("Max")).count(); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java index 2edc0ea1..a231174b 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/PlayerTest.java @@ -8,7 +8,7 @@ class PlayerTest { @Test void shouldReturnUsername() { - Player player = new Player("Max", false); + Player player = new Player("Max", false, null); assertEquals("Max", player.username()); } From a867f2a89a52bd6119cf9736e0960433aebb3c1d Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 14:04:24 +0200 Subject: [PATCH 131/172] feat: prevent fake players by verifying UUID on rejoin --- .../codenames/backend/lobby/domain/Lobby.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 229e5caa..9b8a5fc8 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.UUID; import lombok.Getter; /** @@ -49,19 +48,37 @@ public Lobby(String lobbyCode, String username) { * @param isHost whether the player is the host of the lobby * @return {@code true} if the player was added, {@code false} otherwise */ - public Player addPlayer(String username, boolean isHost) { - boolean alreadyExists = playerList.stream().anyMatch(p -> p.username().equals(username)); - if (alreadyExists || playerList.size() >= MAX_PLAYERS) { + public Player addPlayer(String username, boolean isHost, String requestedUuid) { + java.util.Optional existingPlayerOpt = playerList.stream() + .filter(p -> p.username().equals(username)) + .findFirst(); + + if (existingPlayerOpt.isPresent()) { + Player existingPlayer = existingPlayerOpt.get(); + + if (existingPlayer.uuid().equals(requestedUuid)) { + return existingPlayer; + } + return null; } - String newUuid = UUID.randomUUID().toString(); + if (playerList.size() >= MAX_PLAYERS) { + return null; + } + + String newUuid = java.util.UUID.randomUUID().toString(); Player newPlayer = new Player(username, isHost, newUuid); - playerList.add(new Player(username, isHost, null)); + playerList.add(newPlayer); + return newPlayer; } + public Player addPlayer(String username, boolean isHost) { + return addPlayer(username, isHost, null); + } + /** * Adds a player to the lobby if capacity allows and the username is unique. Calls {@link * #addPlayer(String, boolean)} with {@code false} as the second argument @@ -70,7 +87,7 @@ public Player addPlayer(String username, boolean isHost) { * @return {@code true} if the player was added, {@code false} otherwise */ public Player addPlayer(String username, String uuid) { - return addPlayer(username, false); + return addPlayer(username, false, uuid); } /** From 8cf89addd9a3c3efb5a73b2662cc3c0406476b7c Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 14:24:39 +0200 Subject: [PATCH 132/172] feat: restrict joins for running games --- .../backend/lobby/api/LobbyController.java | 30 ++++++------ .../lobby/application/LobbyService.java | 48 ++++++++++++++----- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 66d7b77f..f83cc8d7 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -47,11 +47,10 @@ public ResponseEntity createLobby(@RequestParam String username) String lobbyCode = service.createLobby(username); if (lobbyCode == null || lobbyCode.isBlank()) { return ResponseEntity.internalServerError() - .body(new LobbyResponse("Error while creating lobby.", "", null, false, null)); // 5. Parameter null + .body(new LobbyResponse("Error while creating lobby.", "", null, false, null)); } else { List players = service.getPlayersDto(lobbyCode); - // UUID des Hosts abrufen und mitsenden Player host = service.getPlayer(lobbyCode, username); String hostUuid = (host != null) ? host.uuid() : null; @@ -69,18 +68,19 @@ public ResponseEntity createLobby(@RequestParam String username) */ @GetMapping("/{lobbyCode}/join") public ResponseEntity joinLobby( - @RequestParam String username, @PathVariable String lobbyCode) { + @RequestParam String username, + @PathVariable String lobbyCode, + @RequestParam(required = false) String uuid) { - // GEÄNDERT: Nimmt jetzt ein Player-Objekt zurück anstatt boolean - Player joinedPlayer = service.joinLobby(username, lobbyCode, null); + Player joinedPlayer = service.joinLobby(username, lobbyCode, uuid); - if (joinedPlayer != null) { // GEÄNDERT: Prüft, ob das Objekt nicht null ist + if (joinedPlayer != null) { return ResponseEntity.ok( new LobbyResponse( - "Joined Lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, joinedPlayer.uuid())); // UUID an Frontend geschickt + "Joined Lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, joinedPlayer.uuid())); } else { return ResponseEntity.badRequest() - .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); // 5. Parameter null + .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); } } @@ -102,10 +102,10 @@ public ResponseEntity leaveLobby( return ResponseEntity.ok( new LobbyResponse( - "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); // 5. Parameter null + "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); } else { return ResponseEntity.badRequest() - .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); // 5. Parameter null + .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); } } @@ -121,7 +121,7 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode List players = service.getPlayersDto(lobbyCode); boolean isStarted = service.getIsStarted(lobbyCode); return ResponseEntity.ok( - new LobbyResponse("Lobby info retrieved successfully.", lobbyCode, players, isStarted, null)); // 5. Parameter null + new LobbyResponse("Lobby info retrieved successfully.", lobbyCode, players, isStarted, null)); } /** * Handles a request to select a team and role for a player. @@ -141,7 +141,7 @@ public ResponseEntity selectPosition( "Position selected successfully.", lobbyCode, service.getPlayersDto(lobbyCode), - false, null)); // 5. Parameter null + false, null)); } else { return ResponseEntity.badRequest() .body( @@ -149,7 +149,7 @@ public ResponseEntity selectPosition( "Could not assign selected team/role.", lobbyCode, service.getPlayersDto(lobbyCode), - false, null)); // 5. Parameter null + false, null)); } } /** @@ -168,11 +168,11 @@ public ResponseEntity startGame( if (isStarted) { return ResponseEntity.ok( new LobbyResponse( - "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true, null)); // 5. Parameter null + "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true, null)); } return ResponseEntity.badRequest() .body( new LobbyResponse( - "Could not start the game.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); // 5. Parameter null + "Could not start the game.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); } } \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index 3b9b6a5d..e080a13e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -87,22 +87,28 @@ private void addGameManagerForLobby(Lobby lobby, String lobbyCode) { */ public Player joinLobby(String username, String lobbyCode, String uuid) { Lobby lobby = lobbyList.get(lobbyCode); + if (lobby != null) { - log.info("{}: a player has joined", lobbyCode); + if (gameService.isGameStarted(lobbyCode)) { + + Player existingPlayer = lobby.getPlayerList().stream() + .filter(p -> p.username().equals(username)) + .findFirst() + .orElse(null); + + if (existingPlayer == null || !existingPlayer.uuid().equals(uuid)) { + log.warn("{}: join rejected. Game already started and no valid UUID provided for reconnect.", lobbyCode); + return null; + } + log.info("{}: player reconnected to a running game", lobbyCode); + } else { + log.info("{}: a player has joined", lobbyCode); + } + return lobby.addPlayer(username, uuid); } - log.error("{}: an error occurred when joining lobby", lobbyCode); - return null; - } - public Player getPlayer(String lobbyCode, String username) { - Lobby lobby = lobbyList.get(lobbyCode); - if (lobby != null) { - return lobby.getPlayerList().stream() - .filter(p -> p.username().equals(username)) - .findFirst() - .orElse(null); - } + log.error("{}: an error occurred when joining lobby", lobbyCode); return null; } @@ -282,6 +288,24 @@ public Role getPlayerRole(String username, String lobbyCode) { return null; } + /** + * Retrieves a specific player from a lobby. + * + * @param lobbyCode the lobby code + * @param username the username of the player + * @return the Player object, or null if not found + */ + public Player getPlayer(String lobbyCode, String username) { + Lobby lobby = lobbyList.get(lobbyCode); + if (lobby != null) { + return lobby.getPlayerList().stream() + .filter(p -> p.username().equals(username)) + .findFirst() + .orElse(null); + } + return null; + } + /** * The service method for starting a game. This creates a game manager object for the lobby and * checks if the requesting user is liable to start the game. From fabd4842b80298f9c59da4a7382403b2fc75e812 Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 14:58:56 +0200 Subject: [PATCH 133/172] test: add tests for UUID and rejoin Lobby feature --- .../lobby/application/LobbyServiceTest.java | 38 ++++++++++++++++--- .../backend/lobby/domain/LobbyTest.java | 31 +++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index 6fb10336..c887ce52 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -385,13 +385,41 @@ void getLobbySnapshotsShouldReturnAllLobbyPlayerDtos() { assertEquals(2, snapshots.get("ABCDE").size()); PlayerDto host = - snapshots.get("ABCDE").stream() - .filter(player -> player.username().equals("Host")) - .findFirst() - .orElseThrow(); + snapshots.get("ABCDE").stream() + .filter(player -> player.username().equals("Host")) + .findFirst() + .orElseThrow(); assertEquals(Team.RED, host.team()); assertEquals(Role.SPYMASTER, host.role()); assertTrue(host.isHost()); } -} + + @Test + void joinLobbyShouldBlockNewPlayerIfGameStarted() { + when(gameService.isGameStarted("ABCDE")).thenReturn(true); + + Player result = lobbyService.joinLobby("NewPlayer", "ABCDE", null); + + assertNull(result); + } + + @Test + void joinLobbyShouldAllowReconnectIfGameStarted() { + lobbyService.getLobbyList().clear(); + + String lobbyCode = lobbyService.createLobby("Host"); + + when(gameService.isGameStarted(lobbyCode)).thenReturn(false); + + Player p1 = lobbyService.joinLobby("P1", lobbyCode, null); + assertNotNull(p1); + + when(gameService.isGameStarted(lobbyCode)).thenReturn(true); + + Player result = lobbyService.joinLobby("P1", lobbyCode, p1.uuid()); + + assertNotNull(result); + assertEquals(p1.uuid(), result.uuid()); + } +} \ No newline at end of file diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index 50d90a1b..c2d065af 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -129,4 +129,35 @@ void startingTeamShouldBeEitherRedOrBlue() { assertTrue(startingTeam == Team.RED || startingTeam == Team.BLUE); } + + @Test + void addPlayerShouldAssignUuidToNewPlayer() { + Lobby lobby = new Lobby("ABCDE", "Host"); + Player newPlayer = lobby.addPlayer("P1", false); + + assertNotNull(newPlayer.uuid()); + } + + @Test + void addPlayerShouldAllowReconnectWithCorrectUuid() { + Lobby lobby = new Lobby("ABCDE", "Host"); + Player first = lobby.addPlayer("P1", false); + String validUuid = first.uuid(); + + Player reconnected = lobby.addPlayer("P1", false, validUuid); + + assertNotNull(reconnected); + assertEquals(validUuid, reconnected.uuid()); + } + + @Test + void addPlayerShouldBlockReconnectWithWrongUuid() { + Lobby lobby = new Lobby("ABCDE", "Host"); + lobby.addPlayer("P1", false); + + + Player blocked = lobby.addPlayer("P1", false, "WRONG-UUID"); + + assertNull(blocked); + } } From 7a058a453be89cb2cba28b645db0451f972b6b9e Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 15:18:48 +0200 Subject: [PATCH 134/172] test: fix failing test --- .../codenames/backend/database/entity/PlayerEntity.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java index d62d8eb0..75012f4e 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java @@ -8,9 +8,11 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.PrePersist; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.UUID; /** * Persistent entity representing a player record within the database schema. @@ -37,4 +39,11 @@ public class PlayerEntity { private String role; @Column(nullable = false, unique = true) private String uuid; + + @PrePersist + protected void onCreate() { + if (this.uuid == null) { + this.uuid = UUID.randomUUID().toString(); + } + } } From f5ebb298b578f006919db1774aa7f246a0704d6b Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:04:18 +0200 Subject: [PATCH 135/172] Resolve merge conflicts and refactor cheat handling --- .../restoration/RestorationMapper.java | 11 +- .../game/api/GameSocketController.java | 25 +- .../backend/game/api/dto/GameStateDto.java | 2 +- .../game/application/CheatService.java | 46 ++++ .../mapping/DataTransferObjectService.java | 3 + .../restoration/RestorationServiceTest.java | 10 +- .../game/api/GameSocketControllerTest.java | 32 ++- .../game/application/CheatServiceTest.java | 75 +++++ .../game/application/GameServiceTest.java | 6 +- .../SystemStateRecoveryServiceTest.java | 257 ------------------ .../infrastructure/JsonStateStoreTest.java | 173 ------------ .../serialization/SerializationJsonTest.java | 79 ------ 12 files changed, 179 insertions(+), 540 deletions(-) create mode 100644 src/main/java/com/codenames/codenames/backend/game/application/CheatService.java create mode 100644 src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java delete mode 100644 src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java delete mode 100644 src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java delete mode 100644 src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 0771e25d..8d505569 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -56,8 +56,15 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { } int remainingGuesses = gameStateEntity.getRemainingGuesses(); List cardList = mapToCardDto(lobbyEntity); - return new GameStateDto(winner, currentTeam, currentPhase, clueDto, - remainingGuesses, cardList); + return new GameStateDto( + null, + currentTeam, + currentPhase, + clueDto, + remainingGuesses, + cardList, + false, + false); } private List mapToCardDto(LobbyEntity lobbyEntity) { diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index fdc8567d..aa3b0898 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -8,12 +8,10 @@ import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; +import com.codenames.codenames.backend.game.application.CheatService; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.CheatResult; import com.codenames.codenames.backend.game.domain.Clue; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; @@ -28,7 +26,7 @@ public class GameSocketController { private final GameService gameService; - private final LobbyService lobbyService; + private final CheatService cheatService; private final SimpMessagingTemplate messagingTemplate; private final PersistenceService persistenceService; @@ -41,18 +39,18 @@ public class GameSocketController { * @param gameService service responsible for gameplay logic * @param messagingTemplate template used for broadcasting websocket messages * @param persistenceService service used to persist current backend state - * @param lobbyService service responsible for lobby and player information + * @param cheatService service responsible for cheat handling */ public GameSocketController( GameService gameService, SimpMessagingTemplate messagingTemplate, PersistenceService persistenceService, - LobbyService lobbyService) { + CheatService cheatService) { this.gameService = gameService; this.messagingTemplate = messagingTemplate; this.persistenceService = persistenceService; - this.lobbyService = lobbyService; + this.cheatService = cheatService; } /** @@ -63,18 +61,11 @@ public GameSocketController( @MessageMapping("/cheat") public void useCheat(CheatCardMessage message) { - Team team = lobbyService.getPlayerTeam(message.getUsername(), message.getLobbyCode()); - Role role = lobbyService.getPlayerRole(message.getUsername(), message.getLobbyCode()); - - if (team == null || role != Role.OPERATIVE) { - return; - } - CheatResult result = - gameService.useCheat( + cheatService.useCheat( message.getLobbyCode(), - message.getPositions(), - team); + message.getUsername(), + message.getPositions()); if (result == null) { return; diff --git a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java index b5357ae0..9b0e390f 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/dto/GameStateDto.java @@ -19,7 +19,7 @@ public record GameStateDto( Role currentPhase, ClueDto currentClue, int remainingGuesses, - List cardList + List cardList, boolean redTeamCheatUsed, boolean blueTeamCheatUsed) { } \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/application/CheatService.java b/src/main/java/com/codenames/codenames/backend/game/application/CheatService.java new file mode 100644 index 00000000..8b534fdf --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/game/application/CheatService.java @@ -0,0 +1,46 @@ +package com.codenames.codenames.backend.game.application; + +import com.codenames.codenames.backend.game.domain.CheatResult; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.List; +import org.springframework.stereotype.Service; + +/** Service responsible for validating and executing cheat requests. */ +@Service +public class CheatService { + + private final GameService gameService; + private final LobbyService lobbyService; + + /** + * Creates a new cheat service. + * + * @param gameService service responsible for game logic + * @param lobbyService service responsible for lobby/player data + */ + public CheatService(GameService gameService, LobbyService lobbyService) { + this.gameService = gameService; + this.lobbyService = lobbyService; + } + + /** + * Performs a cheat request for a player. + * + * @param lobbyCode the lobby code + * @param username the requesting username + * @param positions selected card positions + * @return the cheat result or null if the request is invalid + */ + public CheatResult useCheat(String lobbyCode, String username, List positions) { + Team team = lobbyService.getPlayerTeam(username, lobbyCode); + Role role = lobbyService.getPlayerRole(username, lobbyCode); + + if (team == null || role != Role.OPERATIVE) { + return null; + } + + return gameService.useCheat(lobbyCode, positions, team); + } +} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java index 69c3be2e..5687b1c2 100644 --- a/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java +++ b/src/main/java/com/codenames/codenames/backend/game/mapping/DataTransferObjectService.java @@ -48,6 +48,7 @@ public GameStateDto createGameStateDto( log.info("A Game is over. Winner is {}.", gameManager.getWinner()); } int remainingGuesses = gameManager.getRemainingGuesses(); + if (gameManager.getCurrentClue() == null) { return new GameStateDto( gameManager.getWinner(), @@ -59,8 +60,10 @@ public GameStateDto createGameStateDto( gameManager.isRedTeamCheatUsed(), gameManager.isBlueTeamCheatUsed()); } + String word = gameManager.getCurrentClue().word(); int guessAmount = gameManager.getCurrentClue().guessAmount(); + return new GameStateDto( gameManager.getWinner(), currentTurn, diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java index 97dc1aba..aa347a47 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationServiceTest.java @@ -57,7 +57,15 @@ void setUp() { cardDtoList = List.of(new CardDto("TEST", null, false)); gameStateDto = - new GameStateDto(redTeam, redTeam, spymaster, new ClueDto("Test", 1), 2, cardDtoList); + new GameStateDto( + redTeam, + redTeam, + spymaster, + new ClueDto("Test", 1), + 2, + cardDtoList, + false, + false); when(transactionManager.getTransaction(any())).thenReturn(transactionStatus); diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index e24e66dc..57da2b26 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -16,10 +16,9 @@ import com.codenames.codenames.backend.game.api.dto.PassTurnMessage; import com.codenames.codenames.backend.game.api.dto.RevealCardMessage; import com.codenames.codenames.backend.game.api.dto.StartGameMessage; +import com.codenames.codenames.backend.game.application.CheatService; import com.codenames.codenames.backend.game.application.GameService; import com.codenames.codenames.backend.game.domain.CheatResult; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -41,7 +40,7 @@ class GameSocketControllerTest { @Mock private PersistenceService persistenceService; - @Mock private LobbyService lobbyService; + @Mock private CheatService cheatService; private GameSocketController controller; @@ -52,7 +51,7 @@ void setUp() { gameService, messagingTemplate, persistenceService, - lobbyService); + cheatService); } @Test @@ -121,7 +120,7 @@ void passTurnShouldPersistAndBroadcastUpdatedState() { } private GameStateDto createGameStateDto() { - return new GameStateDto(null, Team.RED, null, null, List.of(), false, false); + return new GameStateDto(null, Team.RED, null, null, 0, List.of(), false, false); } @Test @@ -133,12 +132,12 @@ void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { CheatResult result = new CheatResult("Die Karte \"Dog\" ist richtig."); - when(lobbyService.getPlayerTeam("Max", LOBBY_CODE)).thenReturn(Team.RED); - when(lobbyService.getPlayerRole("Max", LOBBY_CODE)).thenReturn(Role.OPERATIVE); - when(gameService.useCheat(LOBBY_CODE, List.of(0, 1), Team.RED)).thenReturn(result); + when(cheatService.useCheat(LOBBY_CODE, "Max", List.of(0, 1))).thenReturn(result); controller.useCheat(message); + verify(cheatService).useCheat(LOBBY_CODE, "Max", List.of(0, 1)); + verify(messagingTemplate) .convertAndSendToUser( eq("Max"), @@ -147,4 +146,21 @@ void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { verify(persistenceService).saveSnapShot(LOBBY_CODE); } + + @Test + void useCheatShouldDoNothingWhenResultIsNull() { + CheatCardMessage message = new CheatCardMessage(); + message.setLobbyCode(LOBBY_CODE); + message.setUsername("Max"); + message.setPositions(List.of(0, 1)); + + when(cheatService.useCheat(LOBBY_CODE, "Max", List.of(0, 1))).thenReturn(null); + + controller.useCheat(message); + + verify(cheatService).useCheat(LOBBY_CODE, "Max", List.of(0, 1)); + verify(messagingTemplate, never()) + .convertAndSendToUser(anyString(), anyString(), any(Object.class)); + verify(persistenceService, never()).saveSnapShot(LOBBY_CODE); + } } diff --git a/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java new file mode 100644 index 00000000..536e0e56 --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java @@ -0,0 +1,75 @@ +package com.codenames.codenames.backend.game.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.codenames.codenames.backend.game.domain.CheatResult; +import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Role; +import com.codenames.codenames.backend.lobby.domain.Team; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Tests the functionality of CheatService. */ +class CheatServiceTest { + + private static final String LOBBY_CODE = "ABCDE"; + private static final String USERNAME = "Max"; + + private GameService gameService; + private LobbyService lobbyService; + private CheatService cheatService; + + @BeforeEach + void setUp() { + gameService = mock(GameService.class); + lobbyService = mock(LobbyService.class); + cheatService = new CheatService(gameService, lobbyService); + } + + @Test + void useCheatShouldReturnResultForOperative() { + List positions = List.of(0, 1); + CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig."); + + when(lobbyService.getPlayerTeam(USERNAME, LOBBY_CODE)).thenReturn(Team.RED); + when(lobbyService.getPlayerRole(USERNAME, LOBBY_CODE)).thenReturn(Role.OPERATIVE); + when(gameService.useCheat(LOBBY_CODE, positions, Team.RED)).thenReturn(expected); + + CheatResult result = cheatService.useCheat(LOBBY_CODE, USERNAME, positions); + + assertEquals(expected, result); + verify(gameService).useCheat(LOBBY_CODE, positions, Team.RED); + } + + @Test + void useCheatShouldReturnNullWhenTeamIsMissing() { + List positions = List.of(0, 1); + + when(lobbyService.getPlayerTeam(USERNAME, LOBBY_CODE)).thenReturn(null); + when(lobbyService.getPlayerRole(USERNAME, LOBBY_CODE)).thenReturn(Role.OPERATIVE); + + CheatResult result = cheatService.useCheat(LOBBY_CODE, USERNAME, positions); + + assertNull(result); + verify(gameService, never()).useCheat(LOBBY_CODE, positions, Team.RED); + } + + @Test + void useCheatShouldReturnNullWhenPlayerIsNotOperative() { + List positions = List.of(0, 1); + + when(lobbyService.getPlayerTeam(USERNAME, LOBBY_CODE)).thenReturn(Team.RED); + when(lobbyService.getPlayerRole(USERNAME, LOBBY_CODE)).thenReturn(Role.SPYMASTER); + + CheatResult result = cheatService.useCheat(LOBBY_CODE, USERNAME, positions); + + assertNull(result); + verify(gameService, never()).useCheat(LOBBY_CODE, positions, Team.RED); + } +} \ No newline at end of file diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index 275dce36..bc30e8b9 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -167,9 +168,10 @@ void testUseCheat() { when(mockGameManager.useCheat(positions, redTeam)).thenReturn(expected); - CheatResult result = gameService.useCheat(lobbyCode, positions, redTeam); + CheatResult result = + gameService.useCheat(lobbyCode, positions, redTeam); assertEquals(expected, result); verify(mockGameManager, times(1)).useCheat(positions, redTeam); } -} +} \ No newline at end of file diff --git a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java b/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java deleted file mode 100644 index 8cf2e77c..00000000 --- a/src/test/java/com/codenames/codenames/backend/recovery/application/SystemStateRecoveryServiceTest.java +++ /dev/null @@ -1,257 +0,0 @@ -package com.codenames.codenames.backend.recovery.application; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.codenames.codenames.backend.chat.application.ChatService; -import com.codenames.codenames.backend.game.api.dto.CardDto; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.application.CardGenerator; -import com.codenames.codenames.backend.game.application.ClueValidationService; -import com.codenames.codenames.backend.game.application.GameService; -import com.codenames.codenames.backend.game.domain.Color; -import com.codenames.codenames.backend.game.domain.GameManager; -import com.codenames.codenames.backend.game.domain.GameManagerFactory; -import com.codenames.codenames.backend.game.mapping.DataTransferObjectService; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.application.LobbyCodeGenerator; -import com.codenames.codenames.backend.lobby.application.LobbyService; -import com.codenames.codenames.backend.lobby.domain.Lobby; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.codenames.codenames.backend.recovery.infrastructure.JsonStateStore; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class SystemStateRecoveryServiceTest { - - @TempDir Path tempDir; - - @Test - void recoverOnStartupDoesNothingWhenNoSnapshotExists() { - TestContext context = createContext(tempDir.resolve("state.json")); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupSkipsSnapshotWhenSchemaVersionDiffers() { - TestContext context = createContext(tempDir.resolve("state.json")); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION + 1, Map.of(), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupRestoresLobbiesAndGamesFromSnapshot() { - TestContext context = createContext(tempDir.resolve("state.json")); - List lobbyPlayers = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - GameStateDto gameSnapshot = - new GameStateDto( - null, - Team.RED, - Role.OPERATIVE, - new ClueDto("ANIMAL", 2), - List.of(new CardDto("Dog", Color.RED, true), new CardDto("Cat", Color.BLUE, false)), false, false); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", lobbyPlayers), - Map.of("ABCDE", gameSnapshot)); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - Lobby restoredLobby = context.lobbyService().getLobbyList().get("ABCDE"); - assertEquals(2, restoredLobby.getPlayerList().size()); - assertEquals(Team.RED, restoredLobby.getPlayerTeam("Host")); - assertEquals(Role.SPYMASTER, restoredLobby.getPlayerRole("Host")); - assertEquals(Team.BLUE, restoredLobby.getPlayerTeam("Player")); - assertEquals(Role.OPERATIVE, restoredLobby.getPlayerRole("Player")); - - assertTrue(context.gameService().isGameStarted("ABCDE")); - GameManager restoredGame = context.gameService().getGameState("ABCDE"); - assertEquals(Team.RED, restoredGame.getCurrentTurn()); - assertEquals(Role.OPERATIVE, restoredGame.getCurrentPhase()); - assertEquals("ANIMAL", restoredGame.getCurrentClue().word()); - assertTrue(restoredGame.getCardList().get(0).isGuessed()); - assertFalse(restoredGame.getCardList().get(1).isGuessed()); - } - - @Test - void recoverOnStartupWithCurrentSchemaAndEmptyMapsDoesNothing() { - TestContext context = createContext(tempDir.resolve("state-empty.json")); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupHandlesNullLobbyAndGameMaps() { - TestContext context = createContext(tempDir.resolve("state-null-maps.json")); - SystemSnapshot snapshot = new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, null, null); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupSkipsLobbyWhenSnapshotEntryIsNull() { - TestContext context = createContext(tempDir.resolve("state-null-lobby-entry.json")); - Map> lobbies = new HashMap<>(); - lobbies.put("ABCDE", null); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, lobbies, Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenPlayersListIsNull() { - TestContext context = createContext(tempDir.resolve("state-null-players.json")); - Map> lobbies = new HashMap<>(); - lobbies.put("ABCDE", null); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, lobbies, Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenPlayersListIsEmpty() { - TestContext context = createContext(tempDir.resolve("state-empty-players.json")); - List lobbySnapshot = List.of(); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupSkipsLobbyWhenAllUsernamesAreInvalid() { - TestContext context = createContext(tempDir.resolve("state-invalid-usernames.json")); - List lobbySnapshot = - List.of( - new PlayerDto(" ", Team.RED, Role.SPYMASTER, true), - new PlayerDto(null, Team.BLUE, Role.OPERATIVE, false)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - } - - @Test - void recoverOnStartupRestoresLobbyWhenSomeTeamOrRoleValuesAreMissing() { - TestContext context = createContext(tempDir.resolve("state-missing-team-role.json")); - List lobbySnapshot = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", null, null, false)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), Map.of()); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - Lobby restoredLobby = context.lobbyService().getLobbyList().get("ABCDE"); - assertEquals(Team.RED, restoredLobby.getPlayerTeam("Host")); - assertEquals(Role.SPYMASTER, restoredLobby.getPlayerRole("Host")); - assertNull(restoredLobby.getPlayerTeam("Player")); - assertNull(restoredLobby.getPlayerRole("Player")); - } - - @Test - void recoverOnStartupRestoresOnlyGamesWhenLobbiesMapIsNull() { - TestContext context = createContext(tempDir.resolve("state-lobbies-null-games-present.json")); - GameStateDto gameSnapshot = - new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, List.of(new CardDto("Tree", Color.BLUE, false)), false, false); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, null, Map.of("ABCDE", gameSnapshot)); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().isEmpty()); - assertTrue(context.gameService().isGameStarted("ABCDE")); - } - - @Test - void recoverOnStartupRestoresOnlyLobbiesWhenGamesMapIsNull() { - TestContext context = createContext(tempDir.resolve("state-games-null-lobbies-present.json")); - List lobbySnapshot = List.of(new PlayerDto("Host", Team.RED, Role.SPYMASTER, true)); - SystemSnapshot snapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of("ABCDE", lobbySnapshot), null); - context.stateStore().save(snapshot); - - context.recoveryService().recoverOnStartup(); - - assertTrue(context.lobbyService().getLobbyList().containsKey("ABCDE")); - assertFalse(context.gameService().isGameStarted("ABCDE")); - } - - private TestContext createContext(Path stateFile) { - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - GameManagerFactory gameManagerFactory = - new GameManagerFactory( - new CardGenerator("CodenamesWordlist.txt"), new ClueValidationService()); - GameService gameService = new GameService(gameManagerFactory, new DataTransferObjectService()); - LobbyService lobbyService = - new LobbyService(new LobbyCodeGenerator(), new ChatService(null), gameService); - SystemStateRecoveryService recoveryService = - new SystemStateRecoveryService(stateStore, lobbyService, gameService, gameManagerFactory); - - return new TestContext(stateStore, lobbyService, gameService, recoveryService); - } - - private record TestContext( - JsonStateStore stateStore, - LobbyService lobbyService, - GameService gameService, - SystemStateRecoveryService recoveryService) {} -} diff --git a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java b/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java deleted file mode 100644 index 84989ba9..00000000 --- a/src/test/java/com/codenames/codenames/backend/recovery/infrastructure/JsonStateStoreTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.codenames.codenames.backend.recovery.infrastructure; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.recovery.domain.snapshot.SystemSnapshot; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class JsonStateStoreTest { - - @TempDir Path tempDir; - - @Test - void loadReturnsEmptyWhenStateFileDoesNotExist() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isEmpty()); - } - - @Test - void loadReturnsEmptyWhenStateFileIsEmpty() throws IOException { - Path stateFile = tempDir.resolve("state.json"); - Files.createFile(stateFile); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isEmpty()); - } - - @Test - void saveAndLoadRoundTrip() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - List lobbyPlayers = - List.of( - new PlayerDto("Host", Team.RED, Role.SPYMASTER, true), - new PlayerDto("Player", Team.BLUE, Role.OPERATIVE, false)); - - GameStateDto gameSnapshot = - new GameStateDto( - null, Team.RED, Role.OPERATIVE, new ClueDto("ANIMAL", 2), List.of(), false, false); - - SystemSnapshot expectedSnapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", lobbyPlayers), - Map.of("ABCDE", gameSnapshot)); - - stateStore.save(expectedSnapshot); - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isPresent()); - assertTrue(Files.exists(stateFile)); - - SystemSnapshot actualSnapshot = loadedSnapshot.get(); - assertEquals(SystemSnapshot.CURRENT_SCHEMA_VERSION, actualSnapshot.schemaVersion()); - assertEquals(1, actualSnapshot.lobbies().size()); - assertEquals(1, actualSnapshot.games().size()); - - List actualPlayers = actualSnapshot.lobbies().get("ABCDE"); - assertEquals(2, actualPlayers.size()); - assertEquals("Host", actualPlayers.get(0).username()); - assertEquals(Team.RED, actualPlayers.get(0).team()); - assertEquals(Role.SPYMASTER, actualPlayers.get(0).role()); - assertTrue(actualPlayers.get(0).isHost()); - - GameStateDto actualGameSnapshot = actualSnapshot.games().get("ABCDE"); - assertEquals(Team.RED, actualGameSnapshot.currentTurn()); - assertEquals(Role.OPERATIVE, actualGameSnapshot.currentPhase()); - assertEquals("ANIMAL", actualGameSnapshot.currentClue().word()); - assertEquals(2, actualGameSnapshot.currentClue().guessAmount()); - assertTrue(actualGameSnapshot.cardList().isEmpty()); - } - - @Test - void saveOverwritesPreviousSnapshot() { - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - SystemSnapshot firstSnapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - SystemSnapshot secondSnapshot = - new SystemSnapshot( - SystemSnapshot.CURRENT_SCHEMA_VERSION, - Map.of("ABCDE", List.of()), - Map.of()); - - stateStore.save(firstSnapshot); - stateStore.save(secondSnapshot); - - Optional loadedSnapshot = loadSnapshot(stateStore); - - assertTrue(loadedSnapshot.isPresent()); - assertTrue(loadedSnapshot.get().lobbies().containsKey("ABCDE")); - assertEquals(1, loadedSnapshot.get().lobbies().size()); - } - - @Test - void saveThrowsIllegalStateWhenStateParentCannotBeCreated() throws IOException { - Path fileAsParent = tempDir.resolve("not-a-directory"); - Files.writeString(fileAsParent, "occupied"); - Path stateFile = fileAsParent.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - - assertThrows(IllegalStateException.class, () -> stateStore.save(snapshot)); - } - - @Test - void loadThrowsIllegalStateWhenJsonIsInvalid() throws IOException { - Path stateFile = tempDir.resolve("state.json"); - Files.writeString(stateFile, "{invalid-json"); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), stateFile.toString()); - - assertThrows(IllegalStateException.class, stateStore::load); - } - - @Test - void gettersExposeConfiguredDependencies() { - ObjectMapper mapper = new ObjectMapper(); - Path stateFile = tempDir.resolve("state.json"); - JsonStateStore stateStore = new JsonStateStore(mapper, stateFile.toString()); - - assertSame(mapper, stateStore.getObjectMapper()); - assertEquals(stateFile, stateStore.getStateFilePath()); - assertNotNull(stateStore.getIoLock()); - } - - @Test - void saveWorksWhenStateFileHasNoParentDirectory() throws IOException { - String fileName = "json-state-store-" + System.nanoTime() + ".json"; - Path stateFile = Path.of(fileName); - JsonStateStore stateStore = new JsonStateStore(new ObjectMapper(), fileName); - SystemSnapshot snapshot = - new SystemSnapshot(SystemSnapshot.CURRENT_SCHEMA_VERSION, Map.of(), Map.of()); - - try { - stateStore.save(snapshot); - - assertTrue(Files.exists(stateFile)); - assertTrue(stateStore.load().isPresent()); - } finally { - Files.deleteIfExists(stateFile); - Files.deleteIfExists(Path.of(fileName + ".tmp")); - } - } - - private Optional loadSnapshot(JsonStateStore stateStore) { - return stateStore.load(); - } -} diff --git a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java b/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java deleted file mode 100644 index f674c2a0..00000000 --- a/src/test/java/com/codenames/codenames/backend/serialization/SerializationJsonTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.codenames.codenames.backend.serialization; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.codenames.codenames.backend.game.api.dto.CardDto; -import com.codenames.codenames.backend.game.api.dto.ClueDto; -import com.codenames.codenames.backend.game.api.dto.GameStateDto; -import com.codenames.codenames.backend.game.domain.Card; -import com.codenames.codenames.backend.game.domain.Color; -import com.codenames.codenames.backend.lobby.domain.Role; -import com.codenames.codenames.backend.lobby.domain.Team; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class SerializationJsonTest { - - SerializationJson serializer; - Card card; - List dummyList; - GameStateDto dummyGameState; - ObjectMapper mapper = new ObjectMapper(); - private static final Team redTeam = Team.RED; - private static final Role spymaster = Role.SPYMASTER; - - @BeforeEach - void setUp() { - card = new Card("TEST", Color.RED); - serializer = new SerializationJson(mapper); - - dummyList = List.of(new CardDto("TEST", null, false)); - dummyGameState = - new GameStateDto( - redTeam, - redTeam, - spymaster, - new ClueDto("Test", 1), - dummyList, - false, - false); - } - - @Test - void testSerialize_pass() { - String expectedResult = "{\"winner\":\"RED\",\"currentTurn\":\"RED\"," - + "\"currentPhase\":\"SPYMASTER\",\"currentClue\":{\"word\":\"Test\"," - + "\"guessAmount\":1},\"cardList\":[{\"word\":\"TEST\"," - + "\"color\":null,\"isGuessed\":false}]," - + "\"redTeamCheatUsed\":false," - + "\"blueTeamCheatUsed\":false}"; - String result = serializer.serialize(dummyGameState); - assertEquals(expectedResult, result); - } - - // writeValueAsString throws JsonProcessingException, - // I either need try catch block or have my method throw the exception. - @Test - void testSerialize_exception() throws JsonProcessingException { - ObjectMapper mockMapper = mock(ObjectMapper.class); - SerializationJson exceptionSerializer = new SerializationJson(mockMapper); - var exception = new MockJsonProcessingException("Because of protected constructors"); - when(mockMapper.writeValueAsString(any())).thenThrow(exception); - assertThrows(IllegalStateException.class, () -> exceptionSerializer.serialize(null)); - } - - // Testing exception for writeValueAsString requires passing objectMapper in a constructor. - // We then create this custom exception and throw it. (Solution found on stack overflow) - static class MockJsonProcessingException extends JsonProcessingException { - public MockJsonProcessingException(String message) { - super(message); - } - } -} From a4a9eb6d990c673c6100d614b29ffbed9e3b7de5 Mon Sep 17 00:00:00 2001 From: ad-devel Date: Sun, 14 Jun 2026 19:35:09 +0200 Subject: [PATCH 136/172] feature: Implemented security chain --- pom.xml | 27 ++++---- .../backend/shared/config/SecurityConfig.java | 58 +++++++++++++++++ .../controller/HealthControllerTest.java | 3 + .../lobby/api/LobbyControllerTest.java | 3 + .../shared/config/SecurityConfigTest.java | 65 +++++++++++++++++++ 5 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java create mode 100644 src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java diff --git a/pom.xml b/pom.xml index d20b2dcc..c06821f8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,30 +1,31 @@ - + 4.0.0 org.springframework.boot spring-boot-starter-parent 3.2.5 - + com.codenames Codenames_Backend 0.0.1-SNAPSHOT Codenames_Backend Codenames_Backend - + - + - + - - - - + + + + 17 @@ -58,7 +59,7 @@ org.springframework.boot spring-boot-starter-web - + org.springframework.boot spring-boot-starter-websocket @@ -93,6 +94,10 @@ h2 runtime + + org.springframework.boot + spring-boot-starter-security + diff --git a/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java new file mode 100644 index 00000000..8f136d7d --- /dev/null +++ b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java @@ -0,0 +1,58 @@ +package com.codenames.codenames.backend.shared.config; + +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.cors.CorsConfigurationSource; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Value("${app.allowed-origins:http://localhost:8080,http://10.0.2.2:8080}") + private String[] allowedOrigins; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .authorizeHttpRequests( + auth -> + auth.requestMatchers(HttpMethod.OPTIONS, "/**") + .permitAll() + .requestMatchers("/health") + .permitAll() + .requestMatchers("/lobby/**") + .permitAll() + .requestMatchers("/ws", "/ws/**", "/ws-fallback", "/ws-fallback/**") + .permitAll() + .anyRequest() + .denyAll()) + .httpBasic(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .logout(AbstractHttpConfigurer::disable); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins)); + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } +} diff --git a/src/test/java/com/codenames/codenames/backend/controller/HealthControllerTest.java b/src/test/java/com/codenames/codenames/backend/controller/HealthControllerTest.java index b0d093c9..e6eb0ce6 100644 --- a/src/test/java/com/codenames/codenames/backend/controller/HealthControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/controller/HealthControllerTest.java @@ -3,6 +3,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.codenames.codenames.backend.shared.config.SecurityConfig; +import org.springframework.context.annotation.Import; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -10,6 +12,7 @@ import org.springframework.test.web.servlet.MockMvc; @WebMvcTest(HealthController.class) +@Import(SecurityConfig.class) class HealthControllerTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index a58c68ed..bb09d102 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -20,8 +20,11 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import com.codenames.codenames.backend.shared.config.SecurityConfig; +import org.springframework.context.annotation.Import; @WebMvcTest(LobbyController.class) +@Import(SecurityConfig.class) class LobbyControllerTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java b/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java new file mode 100644 index 00000000..150ed794 --- /dev/null +++ b/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java @@ -0,0 +1,65 @@ +package com.codenames.codenames.backend.shared.config; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.bind.annotation.RestController; + +@WebMvcTest(controllers = SecurityConfigTest.EmptyController.class) +@Import(SecurityConfig.class) +@TestPropertySource(properties = "app.allowed-origins=http://localhost:8080,http://10.0.2.2:8080") +class SecurityConfigTest { + + @Autowired private MockMvc mockMvc; + + @Test + void healthEndpointShouldBePublic() throws Exception { + mockMvc.perform(get("/health")).andExpect(status().isNotFound()); + } + + @Test + void lobbyEndpointShouldBePublic() throws Exception { + mockMvc.perform(get("/lobby/test")).andExpect(status().isNotFound()); + } + + @Test + void lobbyPostShouldNotRequireCsrfToken() throws Exception { + mockMvc + .perform(post("/lobby/test").contentType(MediaType.APPLICATION_JSON).content("{}")) + .andExpect(status().isNotFound()); + } + + @Test + void websocketFallbackEndpointShouldBePublic() throws Exception { + mockMvc.perform(get("/ws-fallback")).andExpect(status().isNotFound()); + } + + @Test + void corsPreflightShouldAllowConfiguredOrigin() throws Exception { + mockMvc + .perform( + options("/lobby/test") + .header("Origin", "http://localhost:8080") + .header("Access-Control-Request-Method", "GET")) + .andExpect(status().isOk()) + .andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:8080")); + } + + @Test + void unknownEndpointShouldBeDenied() throws Exception { + mockMvc.perform(get("/internal")).andExpect(status().isForbidden()); + } + + @RestController + static class EmptyController {} +} From 82ab401241bcf9eb47b864d688907eba9bdfc861 Mon Sep 17 00:00:00 2001 From: ad-devel Date: Sun, 14 Jun 2026 19:58:55 +0200 Subject: [PATCH 137/172] fix: sonar issue Scope CSRF configuration to API endpoints --- .../codenames/backend/shared/config/SecurityConfig.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java index 8f136d7d..8c6f06ab 100644 --- a/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java +++ b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java @@ -10,8 +10,8 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; @Configuration @EnableWebSecurity @@ -22,7 +22,10 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(AbstractHttpConfigurer::disable) + http.csrf( + csrf -> + csrf.ignoringRequestMatchers( + "/lobby/**", "/ws", "/ws/**", "/ws-fallback", "/ws-fallback/**")) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .authorizeHttpRequests( auth -> From 94bc5cb4c8832017da32c08ca297aeed71a10681 Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 21:05:41 +0200 Subject: [PATCH 138/172] fix: add migration for player UUID --- .../resources/db/migration/V2__add_player_uuid.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/resources/db/migration/V2__add_player_uuid.sql diff --git a/src/main/resources/db/migration/V2__add_player_uuid.sql b/src/main/resources/db/migration/V2__add_player_uuid.sql new file mode 100644 index 00000000..1a73e104 --- /dev/null +++ b/src/main/resources/db/migration/V2__add_player_uuid.sql @@ -0,0 +1,12 @@ +ALTER TABLE player + ADD COLUMN uuid VARCHAR(36); + +UPDATE player +SET uuid = gen_random_uuid()::text; + +ALTER TABLE player + ALTER COLUMN uuid SET NOT NULL; + +ALTER TABLE player + ADD CONSTRAINT unique_player_uuid + UNIQUE (uuid); \ No newline at end of file From e49ab6abd6562ba0f13eaf246f7ecfd6be737359 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 21:38:16 +0200 Subject: [PATCH 139/172] feat: add player UUID to persistence mapping --- .../backend/database/persistence/PersistenceMapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 7e0531d9..cda2bf2f 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -53,6 +53,7 @@ private List mapPlayer( playerEntity.setIsHost(players.get(i).isHost()); playerEntity.setTeam(players.get(i).team().name()); playerEntity.setRole(players.get(i).role().name()); + playerEntity.setUuid(players.get(i).uuid()); playerList.add(playerEntity); } return playerList; From 97a2a3dd2b049abc23ef3529773ea56a23a1eeac Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 21:38:38 +0200 Subject: [PATCH 140/172] feat: add player UUID to Player DTO --- .../codenames/codenames/backend/lobby/api/dto/PlayerDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java index 395c7e71..ace76666 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/dto/PlayerDto.java @@ -11,5 +11,5 @@ * @param role the user's current role, this can be null * @param isHost whether the user is host */ -public record PlayerDto(String username, Team team, Role role, boolean isHost) { +public record PlayerDto(String username, Team team, Role role, boolean isHost, String uuid) { } From 581d4b449fb0ba4a3f81cf0cd7f33e52d0a3c429 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 21:48:39 +0200 Subject: [PATCH 141/172] feat: add uuid to restoration mapping --- .../database/restoration/RestorationMapper.java | 6 +++++- .../codenames/backend/lobby/domain/Lobby.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 0771e25d..e5eeaf48 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -92,7 +92,8 @@ private List mapToPlayerDto(LobbyEntity lobbyEntity) { playerEntity.getUsername(), team, role, - playerEntity.getIsHost()); + playerEntity.getIsHost(), + playerEntity.getUuid()); playerList.add(playerDto); } return playerList; @@ -113,6 +114,9 @@ private Lobby buildLobby(String lobbyCode, List players) { if (player.role() != null) { lobby.setPlayerRole(player.username(), player.role()); } + if (player.uuid() != null) { + lobby.setPlayerUuid(player.username(), player.uuid()); + } } return lobby; diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 9b8a5fc8..53ad90d4 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -131,6 +131,23 @@ public void setPlayerRole(String username, Role role) { playerRoles.put(username, role); } + /** + * Sets the UUID for a player by replacing their Player record. + * Used during restoration to preserve UUIDs from the database. + * + * @param username the username of the player + * @param uuid the UUID to assign + */ + public void setPlayerUuid(String username, String uuid) { + for (int i = 0; i < playerList.size(); i++) { + Player p = playerList.get(i); + if (p.username().equals(username)) { + playerList.set(i, new Player(p.username(), p.isHost(), uuid)); + return; + } + } + } + /** * Returns the team of a player. * From b78de6de711209999f707826ce7f80dfa5f84184 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 21:50:29 +0200 Subject: [PATCH 142/172] feat: add parameter to correctly create playerDto in LobbyService --- .../codenames/backend/lobby/application/LobbyService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index e080a13e..c27ce0cf 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -212,7 +212,8 @@ public List getPlayersDto(String lobbyCode) { player.username(), lobby.getPlayerTeam(player.username()), lobby.getPlayerRole(player.username()), - player.isHost())) + player.isHost(), + player.uuid())) .toList(); } return List.of(); From 98181a9d511a6406502ec11c26c1cd9cea13b353 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 21:51:25 +0200 Subject: [PATCH 143/172] test: update test to intake uuid as parameter for PlayerDto --- .../database/persistence/PersistenceMappingTest.java | 4 ++-- .../database/persistence/PersistenceServiceTest.java | 4 ++-- .../codenames/backend/lobby/api/LobbyControllerTest.java | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 907a08ea..7236dbd2 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -52,8 +52,8 @@ void setup() { mockClueValidationService = mock(ClueValidationService.class); gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); - player1 = new PlayerDto("Test1", redTeam, spymaster, true); - player2 = new PlayerDto("Test2", redTeam, operative, false); + player1 = new PlayerDto("Test1", redTeam, spymaster, true, null); + player2 = new PlayerDto("Test2", redTeam, operative, false, null); playerDtoList = List.of(player1, player2); lobbyEntity = diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index d8a7708e..0a788df5 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -77,8 +77,8 @@ void setUp() { gameManager = helperMethodGenerateFullCardList(redColor, redTeam); - player1 = new PlayerDto("Test1", redTeam, spymaster, true); - player2 = new PlayerDto("Test2", redTeam, operative, false); + player1 = new PlayerDto("Test1", redTeam, spymaster, true, null); + player2 = new PlayerDto("Test2", redTeam, operative, false, null); playerDtoList = List.of(player1, player2); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index e20ef83a..dea843fe 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -74,7 +74,7 @@ void joinLobbyShouldReturn200WhenSuccess() throws Exception { @Test void getLobbyInfoShouldReturn200() throws Exception { when(service.getPlayersDto("ABCDE")) - .thenReturn(List.of(new PlayerDto("test", null, null, true))); + .thenReturn(List.of(new PlayerDto("test", null, null, true, null))); String url = "/lobby/ABCDE"; mockMvc.perform(get(url)).andExpect(status().isOk()); } @@ -158,7 +158,7 @@ void selectPositionShouldReturn400WhenAssignmentFails() throws Exception { @Test void getLobbyInfoShouldReturn200WhenLobbyExists() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true), new PlayerDto("Bob", null, null, false)); + List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); @@ -174,7 +174,7 @@ void getLobbyInfoShouldReturn200WhenLobbyExists() throws Exception { @Test void testStartGameReturns200WhenConditionIsMet() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true), new PlayerDto("Bob", null, null, false)); + List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); when(service.startGame("ABCDE", "Alice")).thenReturn(true); @@ -191,7 +191,7 @@ void testStartGameReturns200WhenConditionIsMet() throws Exception { @Test void testStartGameReturns400WhenServiceReturnsFalse() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true), new PlayerDto("Bob", null, null, false)); + List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); when(service.startGame("ABCDE", "Alice")).thenReturn(false); From 501d7f0e3bee1e49bdeebf2fd83cdf18c42ec57f Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 22:17:17 +0200 Subject: [PATCH 144/172] feat: add V3 Flyway Script to update player table and have UUID as PK --- .../db/migration/V3__swap_player_pk_to_uuid.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/resources/db/migration/V3__swap_player_pk_to_uuid.sql diff --git a/src/main/resources/db/migration/V3__swap_player_pk_to_uuid.sql b/src/main/resources/db/migration/V3__swap_player_pk_to_uuid.sql new file mode 100644 index 00000000..2adb844d --- /dev/null +++ b/src/main/resources/db/migration/V3__swap_player_pk_to_uuid.sql @@ -0,0 +1,12 @@ +-- Swap player PK from auto-generated BIGINT id to application-generated UUID +-- No other table references player.id, so no FK rewiring needed + +ALTER TABLE player + DROP COLUMN id CASCADE; + +ALTER TABLE player + ADD PRIMARY KEY (uuid); + +-- V2 Constraint no longer needed since i set it to PK +ALTER TABLE player + DROP CONSTRAINT unique_player_uuid; From 47cfd0ebd30d0759ff13f3483ce7ed48df3dfa19 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 22:17:57 +0200 Subject: [PATCH 145/172] feat: update PlayerEntity class to have UUID as PK --- .../backend/database/entity/PlayerEntity.java | 17 ++--------------- .../database/repository/PlayerRepository.java | 2 +- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java index 75012f4e..c7beedfc 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/PlayerEntity.java @@ -2,17 +2,13 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; -import jakarta.persistence.PrePersist; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.util.UUID; /** * Persistent entity representing a player record within the database schema. @@ -24,8 +20,8 @@ @Table(name = "player") public class PlayerEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Column(nullable = false) + private String uuid; @ManyToOne @JoinColumn(name = "lobby_code", nullable = false) private LobbyEntity lobbyEntity; @@ -37,13 +33,4 @@ public class PlayerEntity { private String team; @Column(length = 9, nullable = false) private String role; - @Column(nullable = false, unique = true) - private String uuid; - - @PrePersist - protected void onCreate() { - if (this.uuid == null) { - this.uuid = UUID.randomUUID().toString(); - } - } } diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 6bb4a82c..26056bb2 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -9,7 +9,7 @@ * Repository class for Lobby that has the default available methods needed to perform SQL queries. */ @Repository -public interface PlayerRepository extends JpaRepository { +public interface PlayerRepository extends JpaRepository { /** * Custom SQL query that is not included in the JpaRepository interface. From a7511e59aef839b97b9e66cd24e107ac99cea8a0 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 22:22:53 +0200 Subject: [PATCH 146/172] test: ensure uuid is mapped correctly --- .../backend/database/persistence/PersistenceMappingTest.java | 5 +++-- .../backend/database/persistence/PersistenceServiceTest.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java index 7236dbd2..ca87bb19 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceMappingTest.java @@ -52,8 +52,8 @@ void setup() { mockClueValidationService = mock(ClueValidationService.class); gameManager = new GameManager(redTeam, mockCardGenerator, mockClueValidationService); - player1 = new PlayerDto("Test1", redTeam, spymaster, true, null); - player2 = new PlayerDto("Test2", redTeam, operative, false, null); + player1 = new PlayerDto("Test1", redTeam, spymaster, true, "test-uuid-1"); + player2 = new PlayerDto("Test2", redTeam, operative, false, "test-uuid-2"); playerDtoList = List.of(player1, player2); lobbyEntity = @@ -86,6 +86,7 @@ void testMapPlayer() { assertTrue(retrievedPlayer1.getIsHost()); assertEquals("RED", retrievedPlayer1.getTeam()); assertEquals("SPYMASTER", retrievedPlayer1.getRole()); + assertEquals("test-uuid-1", retrievedPlayer1.getUuid()); } @Test diff --git a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java index 0a788df5..5cd04897 100644 --- a/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/persistence/PersistenceServiceTest.java @@ -77,8 +77,8 @@ void setUp() { gameManager = helperMethodGenerateFullCardList(redColor, redTeam); - player1 = new PlayerDto("Test1", redTeam, spymaster, true, null); - player2 = new PlayerDto("Test2", redTeam, operative, false, null); + player1 = new PlayerDto("Test1", redTeam, spymaster, true, "test-uuid-1"); + player2 = new PlayerDto("Test2", redTeam, operative, false, "test-uuid-2"); playerDtoList = List.of(player1, player2); @@ -141,6 +141,7 @@ void testSaveSnapshot_player() { assertTrue(playerEntity.getIsHost()); assertEquals("RED", playerEntity.getTeam()); assertEquals("SPYMASTER", playerEntity.getRole()); + assertEquals("test-uuid-1", playerEntity.getUuid()); } @Test From 747b6649545241e32749d82dd4fed3b2896240f5 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 22:29:42 +0200 Subject: [PATCH 147/172] test: update mapping test to check uuid --- .../backend/database/restoration/RestorationMapperTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java index 6c763eff..140b37ee 100644 --- a/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java +++ b/src/test/java/com/codenames/codenames/backend/database/restoration/RestorationMapperTest.java @@ -40,6 +40,7 @@ void testMapToLobbyDto() { playerEntity.setIsHost(true); playerEntity.setTeam("RED"); playerEntity.setRole("OPERATIVE"); + playerEntity.setUuid("test-uuid-1"); List playerList = List.of(playerEntity); lobbyEntity.setPlayerEntities(playerList); @@ -47,8 +48,8 @@ void testMapToLobbyDto() { assertNotNull(lobby); assertEquals("ABCDE", lobby.getLobbyCode()); - assertEquals(1, lobby.getPlayerList().size()); + assertEquals("test-uuid-1", lobby.getPlayerList().get(0).uuid()); } @Test @@ -143,5 +144,4 @@ void testBuildLobby_nullRole() { assertNull(lobby.getPlayerRole("TestPlayer2")); } - } From 236af043974b930d61d8510f789e2bcdee58f647 Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 23:14:43 +0200 Subject: [PATCH 148/172] style: apply google codestyle --- .../backend/lobby/api/LobbyController.java | 40 ++++++++++++++++--- .../lobby/api/LobbySocketController.java | 6 ++- .../lobby/application/LobbyService.java | 2 +- .../codenames/backend/lobby/domain/Lobby.java | 10 ++++- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index f83cc8d7..846982a9 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -55,7 +55,8 @@ public ResponseEntity createLobby(@RequestParam String username) String hostUuid = (host != null) ? host.uuid() : null; return ResponseEntity.ok( - new LobbyResponse("Successfully created Lobby.", lobbyCode, players, false, hostUuid)); + new LobbyResponse( + "Successfully created Lobby.", lobbyCode, players, false, hostUuid)); } } @@ -77,7 +78,12 @@ public ResponseEntity joinLobby( if (joinedPlayer != null) { return ResponseEntity.ok( new LobbyResponse( - "Joined Lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, joinedPlayer.uuid())); + "Joined Lobby successfully.", + lobbyCode, + service.getPlayersDto(lobbyCode), + false, + joinedPlayer.uuid()) + ); } else { return ResponseEntity.badRequest() .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); @@ -102,7 +108,11 @@ public ResponseEntity leaveLobby( return ResponseEntity.ok( new LobbyResponse( - "Left lobby successfully.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); + "Left lobby successfully.", + lobbyCode, service.getPlayersDto(lobbyCode), + false, + null) + ); } else { return ResponseEntity.badRequest() .body(new LobbyResponse(LOBBY_NOT_FOUND, lobbyCode, null, false, null)); @@ -121,7 +131,13 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode List players = service.getPlayersDto(lobbyCode); boolean isStarted = service.getIsStarted(lobbyCode); return ResponseEntity.ok( - new LobbyResponse("Lobby info retrieved successfully.", lobbyCode, players, isStarted, null)); + new LobbyResponse( + "Lobby info retrieved successfully.", + lobbyCode, + players, + isStarted, + null) + ); } /** * Handles a request to select a team and role for a player. @@ -129,6 +145,7 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode * @param request the position selection request containing username, lobby code, team, and role * @return a response indicating whether the selection was successful */ + @PostMapping("/{lobbyCode}/select-position") public ResponseEntity selectPosition( @PathVariable String lobbyCode, @RequestBody PlayerDto request) { @@ -160,6 +177,7 @@ public ResponseEntity selectPosition( * @return a response entity of a lobby response, with isStarted @code true or @code false, * whether the starting was successful or not */ + @GetMapping("/{lobbyCode}/start-game") public ResponseEntity startGame( @PathVariable String lobbyCode, @RequestParam String username) { @@ -168,11 +186,21 @@ public ResponseEntity startGame( if (isStarted) { return ResponseEntity.ok( new LobbyResponse( - "Game is starting now.", lobbyCode, service.getPlayersDto(lobbyCode), true, null)); + "Game is starting now.", + lobbyCode, + service.getPlayersDto(lobbyCode), + true, + null) + ); } return ResponseEntity.badRequest() .body( new LobbyResponse( - "Could not start the game.", lobbyCode, service.getPlayersDto(lobbyCode), false, null)); + "Could not start the game.", + lobbyCode, + service.getPlayersDto(lobbyCode), + false, + null) + ); } } \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index 81939b4b..ea23af3f 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -71,7 +71,11 @@ public void join(JoinMessage message, SimpMessageHeaderAccessor headerAccessor) log.error(e.getMessage()); } - Player joinedPlayer = lobbyService.joinLobby(message.getName(), message.getCode(), message.getUuid()); + Player joinedPlayer = lobbyService.joinLobby( + message.getName(), + message.getCode(), + message.getUuid() + ); boolean joined = (joinedPlayer != null); boolean reconnect = lobbyService.getPlayers(message.getCode()).stream() diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index c27ce0cf..cdeb3275 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -97,7 +97,7 @@ public Player joinLobby(String username, String lobbyCode, String uuid) { .orElse(null); if (existingPlayer == null || !existingPlayer.uuid().equals(uuid)) { - log.warn("{}: join rejected. Game already started and no valid UUID provided for reconnect.", lobbyCode); + log.warn("{}: join rejected. Game started, no valid reconnect UUID.", lobbyCode); return null; } log.info("{}: player reconnected to a running game", lobbyCode); diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 53ad90d4..283021ac 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import lombok.Getter; @@ -68,12 +69,19 @@ public Player addPlayer(String username, boolean isHost, String requestedUuid) { return null; } - String newUuid = java.util.UUID.randomUUID().toString(); + String newUuid = UUID.randomUUID().toString(); Player newPlayer = new Player(username, isHost, newUuid); playerList.add(newPlayer); return newPlayer; } + /** + * Adds a player to lobby. + * + * @param username der Name des Spielers + * @param isHost gibt an, ob der Spieler der Host ist + * @return der erstellte Player + */ public Player addPlayer(String username, boolean isHost) { return addPlayer(username, isHost, null); From 10e1a2f3a6b4e378075c56bf654e1f4432aeaa78 Mon Sep 17 00:00:00 2001 From: 5eli Date: Sun, 14 Jun 2026 23:31:46 +0200 Subject: [PATCH 149/172] style: apply google codestyle to tests --- .../game/domain/GameManagerFactoryTest.java | 9 +++++++- .../lobby/api/LobbyControllerTest.java | 21 ++++++++++++------- .../lobby/api/LobbySocketControllerTest.java | 15 ++++++++----- .../backend/lobby/domain/LobbyTest.java | 8 +++++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java index 7c0f3ac3..906920d3 100644 --- a/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/domain/GameManagerFactoryTest.java @@ -63,7 +63,14 @@ void testCreateFromSnapshotWithClue() { void testCreateFromSnapshotWithoutClue() { GameStateDto snapshot = new GameStateDto( - null, Team.BLUE, Role.SPYMASTER, null, 0, List.of(new CardDto("Tree", Color.BLUE, false))); + null, + Team.BLUE, + Role.SPYMASTER, + null, + 0, + List.of(new CardDto("Tree", + Color.BLUE, false)) + ); GameManager recovered = gameManagerFactory.createFromSnapshot(snapshot); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index dea843fe..ad19d70e 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -8,9 +8,9 @@ import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.application.LobbyService; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; -import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,7 +29,11 @@ class LobbyControllerTest { @Test void createLobbyShouldReturn200() throws Exception { when(service.createLobby("TestUser")).thenReturn("ABCDE"); - when(service.getPlayer("ABCDE", "TestUser")).thenReturn(new Player("TestUser", true, "mock-uuid")); + when(service.getPlayer("ABCDE", + "TestUser")).thenReturn(new Player("TestUser", + true, + "mock-uuid") + ); mockMvc .perform(get("/lobby/create").param("username", "TestUser")) @@ -127,7 +131,7 @@ void selectPositionShouldReturn200whenSuccess() throws Exception { "role": "SPYMASTER", "isHost": "true" } - """)) + """)) .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("Position selected successfully.")) .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); @@ -149,7 +153,7 @@ void selectPositionShouldReturn400WhenAssignmentFails() throws Exception { "role": "SPYMASTER", "isHost": "true" } - """)) + """)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("Could not assign selected team/role.")) .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); @@ -158,7 +162,8 @@ void selectPositionShouldReturn400WhenAssignmentFails() throws Exception { @Test void getLobbyInfoShouldReturn200WhenLobbyExists() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); + List.of(new PlayerDto("Alice", null, null, true, null), + new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); @@ -174,7 +179,8 @@ void getLobbyInfoShouldReturn200WhenLobbyExists() throws Exception { @Test void testStartGameReturns200WhenConditionIsMet() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); + List.of(new PlayerDto("Alice", null, null, true, null), + new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); when(service.startGame("ABCDE", "Alice")).thenReturn(true); @@ -191,7 +197,8 @@ void testStartGameReturns200WhenConditionIsMet() throws Exception { @Test void testStartGameReturns400WhenServiceReturnsFalse() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true, null), new PlayerDto("Bob", null, null, false, null)); + List.of(new PlayerDto("Alice", null, null, true, null), + new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); when(service.startGame("ABCDE", "Alice")).thenReturn(false); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java index f2a85b52..09db3d22 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbySocketControllerTest.java @@ -60,9 +60,11 @@ void shouldRegisterJoinAndRegisterSession() { accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn(new Player("Max", true, "mock-uuid")); + when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn( + new Player("Max", true, "mock-uuid")); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "mock-uuid"))); + when(lobbyService.getPlayers("ABCDE")).thenReturn( + List.of(new Player("Max", true, "mock-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); @@ -125,8 +127,10 @@ void shouldUseSessionAttributesFallbackWhenSessionIdIsNull() { attrs.put("sessionId", "123"); accessor.setSessionAttributes(attrs); - when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn(new Player("Max", true, "mock-uuid")); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "mock-uuid"))); + when(lobbyService.joinLobby("Max", "ABCDE", null)).thenReturn( + new Player("Max", true, "mock-uuid")); + when(lobbyService.getPlayers("ABCDE")).thenReturn( + List.of(new Player("Max", true, "mock-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); @@ -154,7 +158,8 @@ void shouldTreatExistingPlayerAsReconnectWhenJoinReturnsFalse() { accessor.setSessionAttributes(attrs); when(lobbyService.joinLobby("Max", "ABCDE", "existing-uuid")).thenReturn(null); - when(lobbyService.getPlayers("ABCDE")).thenReturn(List.of(new Player("Max", true, "existing-uuid"))); + when(lobbyService.getPlayers("ABCDE")).thenReturn( + List.of(new Player("Max", true, "existing-uuid"))); when(gameService.getCurrentGameState("ABCDE")).thenReturn(createGameStatePayload()); controller.join(msg, accessor); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index c2d065af..a639ceba 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -1,8 +1,12 @@ package com.codenames.codenames.backend.lobby.domain; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * Tests for {@link Lobby}. From 74cf70037b2773bd8ed596f7fbba85781765f3ac Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 23:52:25 +0200 Subject: [PATCH 150/172] test: add test for setting player UUID for restoration --- .../restoration/RestorationMapper.java | 4 ++++ .../codenames/backend/lobby/domain/Lobby.java | 3 ++- .../backend/lobby/domain/LobbyTest.java | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index e5eeaf48..2addf2e9 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -101,6 +101,9 @@ private List mapToPlayerDto(LobbyEntity lobbyEntity) { private Lobby buildLobby(String lobbyCode, List players) { String hostUsername = findHostUsername(players); + // the way uuid is generated is a bit roundabout, but I do not have the time to refactor it + // and I do not want to touch the code if it works (Lobby.java) + // since constructor uses the two parameter method -> reroute to 3 parameter -> always null UUID Lobby lobby = new Lobby(lobbyCode, hostUsername); @@ -115,6 +118,7 @@ private Lobby buildLobby(String lobbyCode, List players) { lobby.setPlayerRole(player.username(), player.role()); } if (player.uuid() != null) { + // Solution: we overwrite all player records with data loaded from DB lobby.setPlayerUuid(player.username(), player.uuid()); } } diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 283021ac..b7618d84 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -150,11 +150,12 @@ public void setPlayerUuid(String username, String uuid) { for (int i = 0; i < playerList.size(); i++) { Player p = playerList.get(i); if (p.username().equals(username)) { + // caller loops through all players in list on match we overwrite playerList.set(i, new Player(p.username(), p.isHost(), uuid)); return; } } - } + } // refer RestorationMapper as for why we overwrite player records /** * Returns the team of a player. diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index a639ceba..5cfaf72d 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -164,4 +165,23 @@ void addPlayerShouldBlockReconnectWithWrongUuid() { assertNull(blocked); } + + @Test + void testSetPlayerUuid_playerFound(){ + Lobby lobby = new Lobby("ABCDE", "Host"); + lobby.setPlayerUuid("Host", "test-uuid-1"); + + Player player1 = lobby.getPlayerList().get(0); + assertEquals("test-uuid-1", player1.uuid()); + } + + @Test + void testSetPlayerUuid_playerNotFound(){ + Lobby lobby = new Lobby("ABCDE", "Host"); + lobby.setPlayerUuid("NotHost", "test-uuid-1"); + // if i don't hit the same username, it should not be overridden + + Player player1 = lobby.getPlayerList().get(0); + assertNotEquals("test-uuid-1", player1.uuid()); + } } From 081493c9084b7214016e90380d97ab7fada96b46 Mon Sep 17 00:00:00 2001 From: XtophB Date: Sun, 14 Jun 2026 23:53:08 +0200 Subject: [PATCH 151/172] style: add spacing before { --- .../codenames/codenames/backend/lobby/domain/LobbyTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index 5cfaf72d..61c6495c 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -160,14 +160,13 @@ void addPlayerShouldBlockReconnectWithWrongUuid() { Lobby lobby = new Lobby("ABCDE", "Host"); lobby.addPlayer("P1", false); - Player blocked = lobby.addPlayer("P1", false, "WRONG-UUID"); assertNull(blocked); } @Test - void testSetPlayerUuid_playerFound(){ + void testSetPlayerUuid_playerFound() { Lobby lobby = new Lobby("ABCDE", "Host"); lobby.setPlayerUuid("Host", "test-uuid-1"); @@ -176,7 +175,7 @@ void testSetPlayerUuid_playerFound(){ } @Test - void testSetPlayerUuid_playerNotFound(){ + void testSetPlayerUuid_playerNotFound() { Lobby lobby = new Lobby("ABCDE", "Host"); lobby.setPlayerUuid("NotHost", "test-uuid-1"); // if i don't hit the same username, it should not be overridden From 7878e5c916dac53ad002b25df4e2f16ae23b6f2e Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:25:44 +0200 Subject: [PATCH 152/172] Persist cheat flags for game state recovery --- .../codenames/backend/database/entity/GameStateEntity.java | 4 ++++ .../backend/database/persistence/PersistenceMapper.java | 7 +++++++ .../backend/database/restoration/RestorationMapper.java | 5 ++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java b/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java index ee0832bb..2fd26858 100644 --- a/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java +++ b/src/main/java/com/codenames/codenames/backend/database/entity/GameStateEntity.java @@ -37,4 +37,8 @@ public class GameStateEntity { private int clueGuessAmount; @Column(name = "remaining_guesses") private int remainingGuesses; + @Column(name = "red_team_cheat_used", nullable = false) + private boolean redTeamCheatUsed; + @Column(name = "blue_team_cheat_used", nullable = false) + private boolean blueTeamCheatUsed; } diff --git a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java index 7e0531d9..7eb4e6d7 100644 --- a/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/persistence/PersistenceMapper.java @@ -78,6 +78,13 @@ private GameStateEntity mapGameState( gameStateEntity.setClueGuessAmount(0); gameStateEntity.setRemainingGuesses(0); } + + gameStateEntity.setRedTeamCheatUsed( + gameManager.isRedTeamCheatUsed()); + + gameStateEntity.setBlueTeamCheatUsed( + gameManager.isBlueTeamCheatUsed()); + return gameStateEntity; } diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 8d505569..3bf4c41c 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -46,7 +46,6 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { throw new IllegalStateException("Game state entity is null"); } - Team winner = null; // valueOf takes string and returns enum of that string Team currentTeam = Team.valueOf(gameStateEntity.getCurrentTurn()); Role currentPhase = Role.valueOf(gameStateEntity.getCurrentPhase()); @@ -63,8 +62,8 @@ public GameStateDto mapToGameStateDto(LobbyEntity lobbyEntity) { clueDto, remainingGuesses, cardList, - false, - false); + gameStateEntity.isRedTeamCheatUsed(), + gameStateEntity.isBlueTeamCheatUsed()); } private List mapToCardDto(LobbyEntity lobbyEntity) { From 51dcfc278c28d096d2172655c31c88b406a87180 Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:41:07 +0200 Subject: [PATCH 153/172] Fix Sonar issues --- .../backend/game/api/GameSocketControllerTest.java | 7 +++---- .../backend/game/application/GameServiceTest.java | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 57da2b26..358ac018 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -2,7 +2,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -140,9 +139,9 @@ void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { verify(messagingTemplate) .convertAndSendToUser( - eq("Max"), - eq("/queue/system"), - eq(new ChatDto("System", result.message(), ChatMessageType.SYSTEM))); + "Max", + "/queue/system", + new ChatDto("System", result.message(), ChatMessageType.SYSTEM)); verify(persistenceService).saveSnapShot(LOBBY_CODE); } diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index bc30e8b9..a5e8fd75 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; From 1ca23e3ee17eceee8b0120b312b4969c26d9204f Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:24:36 +0200 Subject: [PATCH 154/172] Add migration for cheat flags --- .../db/migration/V2__add_cheat_flags_to_game_state.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql diff --git a/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql b/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql new file mode 100644 index 00000000..664a2dc9 --- /dev/null +++ b/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql @@ -0,0 +1,5 @@ +ALTER TABLE game_state + ADD COLUMN red_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; + +ALTER TABLE game_state + ADD COLUMN blue_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; \ No newline at end of file From ccd2b9b99fcd4e2e32cc938fee821a86c8519011 Mon Sep 17 00:00:00 2001 From: ad-devel Date: Mon, 15 Jun 2026 19:29:46 +0200 Subject: [PATCH 155/172] fix: allowed-origins and SockJS changes according to feedback --- .../backend/shared/config/SecurityConfig.java | 9 +++------ .../backend/shared/config/WebSocketConfig.java | 1 - src/main/resources/application.yaml | 2 +- .../shared/config/SecurityConfigTest.java | 5 +++++ .../shared/config/WebSocketConfigTest.java | 17 ++++------------- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java index 8c6f06ab..9cc97b20 100644 --- a/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java +++ b/src/main/java/com/codenames/codenames/backend/shared/config/SecurityConfig.java @@ -17,15 +17,12 @@ @EnableWebSecurity public class SecurityConfig { - @Value("${app.allowed-origins:http://localhost:8080,http://10.0.2.2:8080}") + @Value("${app.allowed-origins}") private String[] allowedOrigins; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf( - csrf -> - csrf.ignoringRequestMatchers( - "/lobby/**", "/ws", "/ws/**", "/ws-fallback", "/ws-fallback/**")) + http.csrf(csrf -> csrf.ignoringRequestMatchers("/lobby/**", "/ws-fallback", "/ws-fallback/**")) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .authorizeHttpRequests( auth -> @@ -35,7 +32,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .permitAll() .requestMatchers("/lobby/**") .permitAll() - .requestMatchers("/ws", "/ws/**", "/ws-fallback", "/ws-fallback/**") + .requestMatchers("/ws-fallback", "/ws-fallback/**") .permitAll() .anyRequest() .denyAll()) diff --git a/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java b/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java index 985471e4..e7c05eff 100644 --- a/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java +++ b/src/main/java/com/codenames/codenames/backend/shared/config/WebSocketConfig.java @@ -42,7 +42,6 @@ public void configureMessageBroker(MessageBrokerRegistry config) { */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/ws").setAllowedOrigins(allowedOrigins).withSockJS(); registry.addEndpoint("/ws-fallback").setAllowedOrigins(allowedOrigins); } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3a63f2d2..71a98f69 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -21,4 +21,4 @@ spring: enabled: true app: - allowed-origins: "http://localhost:8080,http://10.0.2.2:8080" \ No newline at end of file + allowed-origins: ${APP_ALLOWED_ORIGINS:http://localhost:8080,http://10.0.2.2:8080} \ No newline at end of file diff --git a/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java b/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java index 150ed794..e4a86f08 100644 --- a/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/config/SecurityConfigTest.java @@ -39,6 +39,11 @@ void lobbyPostShouldNotRequireCsrfToken() throws Exception { .andExpect(status().isNotFound()); } + @Test + void sockJsEndpointShouldBeDenied() throws Exception { + mockMvc.perform(get("/ws")).andExpect(status().isForbidden()); + } + @Test void websocketFallbackEndpointShouldBePublic() throws Exception { mockMvc.perform(get("/ws-fallback")).andExpect(status().isNotFound()); diff --git a/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java b/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java index 99147ba4..461d96bf 100644 --- a/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java +++ b/src/test/java/com/codenames/codenames/backend/shared/config/WebSocketConfigTest.java @@ -5,13 +5,13 @@ import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; /** Unit tests for {@link WebSocketConfig}. */ class WebSocketConfigTest { + @Test void shouldConfigureMessageBroker() { WebSocketConfig config = new WebSocketConfig(); @@ -25,7 +25,7 @@ void shouldConfigureMessageBroker() { } @Test - void shouldRegisterStompEndpoints() { + void shouldRegisterRawWebSocketEndpoint() { WebSocketConfig config = new WebSocketConfig(); StompEndpointRegistry registry = mock(StompEndpointRegistry.class); @@ -35,25 +35,16 @@ void shouldRegisterStompEndpoints() { org.springframework.web.socket.config.annotation.StompWebSocketEndpointRegistration .class); - when(registry.addEndpoint("/ws")).thenReturn(endpointRegistration); - when(registry.addEndpoint("/ws-fallback")).thenReturn(endpointRegistration); - - var sockJsRegistration = - mock(org.springframework.web.socket.config.annotation.SockJsServiceRegistration.class); - String[] origins = new String[] {"http://localhost:8080", "http://10.0.2.2:8080"}; ReflectionTestUtils.setField(config, "allowedOrigins", origins); + when(registry.addEndpoint("/ws-fallback")).thenReturn(endpointRegistration); when(endpointRegistration.setAllowedOrigins(origins)).thenReturn(endpointRegistration); - when(endpointRegistration.withSockJS()).thenReturn(sockJsRegistration); - config.registerStompEndpoints(registry); - verify(registry).addEndpoint("/ws"); - verify(endpointRegistration, Mockito.times(2)).setAllowedOrigins(origins); - verify(endpointRegistration).withSockJS(); verify(registry).addEndpoint("/ws-fallback"); + verify(endpointRegistration).setAllowedOrigins(origins); } } From b1f08911203595eb5a19efe5d79fffe90389d10c Mon Sep 17 00:00:00 2001 From: XtophB Date: Mon, 15 Jun 2026 20:22:08 +0200 Subject: [PATCH 156/172] style: add imports instead of manually calling packages --- .../codenames/backend/lobby/domain/Lobby.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index b7618d84..ee1acecd 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -1,10 +1,12 @@ package com.codenames.codenames.backend.lobby.domain; +import static java.util.UUID.randomUUID; + import java.security.SecureRandom; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import lombok.Getter; @@ -51,7 +53,7 @@ public Lobby(String lobbyCode, String username) { */ public Player addPlayer(String username, boolean isHost, String requestedUuid) { - java.util.Optional existingPlayerOpt = playerList.stream() + Optional existingPlayerOpt = playerList.stream() .filter(p -> p.username().equals(username)) .findFirst(); @@ -69,7 +71,7 @@ public Player addPlayer(String username, boolean isHost, String requestedUuid) { return null; } - String newUuid = UUID.randomUUID().toString(); + String newUuid = randomUUID().toString(); Player newPlayer = new Player(username, isHost, newUuid); playerList.add(newPlayer); @@ -140,8 +142,7 @@ public void setPlayerRole(String username, Role role) { } /** - * Sets the UUID for a player by replacing their Player record. - * Used during restoration to preserve UUIDs from the database. + * Sets the UUID for a player by replacing their Player record, used by restoration of UUID. * * @param username the username of the player * @param uuid the UUID to assign From 9e1c1344af894ad689eee78f8f9d7350b350d44f Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 10:48:48 +0200 Subject: [PATCH 157/172] fix: authenticate leave and start game requests using UUID --- .../backend/lobby/api/LobbyController.java | 12 +++++----- .../lobby/application/LobbyService.java | 24 +++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 846982a9..89c72113 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -93,15 +93,15 @@ public ResponseEntity joinLobby( /** * Handles a request to leave a lobby. * - * @param username the username of the player + * @param uuid the UUID of the player * @param lobbyCode the lobby code identifying the lobby * @return a response indicating whether the operation was successful */ @GetMapping("/{lobbyCode}/leave") public ResponseEntity leaveLobby( - @PathVariable String lobbyCode, @RequestParam String username) { + @PathVariable String lobbyCode, @RequestParam String uuid) { boolean wasStarted = service.getIsStarted(lobbyCode); - boolean left = service.leaveLobby(username, lobbyCode); + boolean left = service.leaveLobby(uuid, lobbyCode); if (left) { service.checkLobbyStillHasPlayers(lobbyCode); @@ -173,15 +173,15 @@ public ResponseEntity selectPosition( * Endpoint for starting a game, this is the last http-request only the host can make. * * @param lobbyCode the unique lobby code - * @param username the name of the requesting user + * @param uuid the UUID of the requesting user * @return a response entity of a lobby response, with isStarted @code true or @code false, * whether the starting was successful or not */ @GetMapping("/{lobbyCode}/start-game") public ResponseEntity startGame( - @PathVariable String lobbyCode, @RequestParam String username) { - boolean isStarted = service.startGame(lobbyCode, username); + @PathVariable String lobbyCode, @RequestParam String uuid) { + boolean isStarted = service.startGame(lobbyCode, uuid); if (isStarted) { return ResponseEntity.ok( diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index cdeb3275..a59b2cbc 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -125,14 +125,14 @@ public void restoreLobby(String lobbyCode, Lobby lobby) { /** * Removes a player from a lobby. * - * @param username the username of the player + * @param uuid the uuid of the player * @param lobbyCode the lobby code identifying the lobby * @return {@code true} if the player was removed, {@code false} if the lobby does not exist */ - public boolean leaveLobby(String username, String lobbyCode) { + public boolean leaveLobby(String uuid, String lobbyCode) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { - lobby.removePlayer(username); + lobby.removePlayer(uuid); log.info("{}: a player left", lobbyCode); return true; } @@ -312,14 +312,18 @@ public Player getPlayer(String lobbyCode, String username) { * checks if the requesting user is liable to start the game. * * @param lobbyCode the unique lobby code - * @param username the name of the requesting user + * @param uuid the uuid of the requesting user * @return if starting was successful */ - public boolean startGame(String lobbyCode, String username) { + public boolean startGame(String lobbyCode, String uuid) { boolean isStarted = - !lobbyCode.isBlank() && !username.isBlank() && Objects.equals(getHost(lobbyCode), username); - Lobby lobby = lobbyList.get(lobbyCode); - addGameManagerForLobby(lobby, lobbyCode); + !lobbyCode.isBlank() + && !uuid.isBlank() + && Objects.equals(getHost(lobbyCode), uuid); + if (isStarted) { + Lobby lobby = lobbyList.get(lobbyCode); + addGameManagerForLobby(lobby, lobbyCode); + } log.info("{}: Game start requested, returning: {}", lobbyCode, isStarted); return isStarted; @@ -329,7 +333,7 @@ public boolean startGame(String lobbyCode, String username) { * This method computes the host of a lobby. * * @param lobbyCode the unique lobby code - * @return the username of the host + * @return the UUID of the host */ public String getHost(String lobbyCode) { if (lobbyCode == null || lobbyCode.isBlank()) { @@ -341,7 +345,7 @@ public String getHost(String lobbyCode) { } for (Player p : players) { if (p.isHost()) { - return p.username(); + return p.uuid(); } } return ""; From 44b1f9596739ce03737d4711dca530a098d48886 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 11:06:58 +0200 Subject: [PATCH 158/172] fix: remove unnecessary null-check --- .../codenames/backend/lobby/api/LobbyController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 89c72113..fc41a141 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -5,6 +5,7 @@ import com.codenames.codenames.backend.lobby.application.LobbyService; import com.codenames.codenames.backend.lobby.domain.Player; import java.util.List; +import java.util.Objects; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -51,8 +52,10 @@ public ResponseEntity createLobby(@RequestParam String username) } else { List players = service.getPlayersDto(lobbyCode); - Player host = service.getPlayer(lobbyCode, username); - String hostUuid = (host != null) ? host.uuid() : null; + Player host = Objects.requireNonNull( + service.getPlayer(lobbyCode, username)); + + String hostUuid = host.uuid(); return ResponseEntity.ok( new LobbyResponse( From 4e30486d0f7621c57d48a21baea4d71baa5336d8 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 11:10:44 +0200 Subject: [PATCH 159/172] fix: enforce UUID validation for player reconnects --- .../codenames/backend/lobby/api/LobbySocketController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java index ea23af3f..803a9926 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbySocketController.java @@ -79,7 +79,7 @@ public void join(JoinMessage message, SimpMessageHeaderAccessor headerAccessor) boolean joined = (joinedPlayer != null); boolean reconnect = lobbyService.getPlayers(message.getCode()).stream() - .anyMatch(player -> player.username().equals(message.getName())); + .anyMatch(player -> player.uuid().equals(message.getUuid())); if (!joined && !reconnect) { messagingTemplate.convertAndSend("/topic/errors/" + sessionId, "Join failed"); From b38cb4c827054c1536d9fa7f2f4aade23fa19088 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 11:17:22 +0200 Subject: [PATCH 160/172] fix: update javaDoc --- .../com/codenames/codenames/backend/lobby/domain/Lobby.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index ee1acecd..36e54289 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -45,11 +45,12 @@ public Lobby(String lobbyCode, String username) { } /** - * Adds a player to the lobby if capacity allows and the username is unique. + * Adds a new player to the lobby or reconnects an existing player. * * @param username the username of the player * @param isHost whether the player is the host of the lobby - * @return {@code true} if the player was added, {@code false} otherwise + * @param requestedUuid the UUID provided by the client to verify a reconnect + * @return {@link Player} if the player was added or reconnected, {@code null} otherwise */ public Player addPlayer(String username, boolean isHost, String requestedUuid) { From 40bf4f55f00375cf06cbe00e21d82c68c6e71130 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 12:20:25 +0200 Subject: [PATCH 161/172] test: align tests with new UUID validation logic --- .../lobby/api/LobbyControllerTest.java | 87 ++++++++++++------- .../lobby/application/LobbyServiceTest.java | 13 ++- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index ad19d70e..a5404cea 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -1,5 +1,6 @@ package com.codenames.codenames.backend.lobby.api; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -67,12 +68,26 @@ void createLobbyNullLobbyCode() throws Exception { @Test void joinLobbyShouldReturn200WhenSuccess() throws Exception { when(service.joinLobby("TestUser", "ABCDE", null)) - .thenReturn(new Player("TestUser", false, "mock-uuid-123")); + .thenReturn(new Player("TestUser", false, "mock-uuid-123")); mockMvc - .perform(get("/lobby/ABCDE/join").param("username", "TestUser")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); + .perform(get("/lobby/ABCDE/join").param("username", "TestUser")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); + } + + @Test + void joinLobbyShouldReturn200WhenReconnectSuccess() throws Exception { + when(service.joinLobby("TestUser", "ABCDE", "mock-uuid-123")) + .thenReturn(new Player("TestUser", false, "mock-uuid-123")); + + mockMvc + .perform( + get("/lobby/ABCDE/join") + .param("username", "TestUser") + .param("uuid", "mock-uuid-123")) // <-- Hier auf "uuid" geändert + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Joined Lobby successfully.")); } @Test @@ -88,20 +103,22 @@ void joinLobbyShouldReturn400WhenNotFound() throws Exception { when(service.joinLobby("TestUser", "XXXXX", null)).thenReturn(null); mockMvc - .perform(get("/lobby/XXXXX/join").param("username", "TestUser")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value("Could not find lobby.")); + .perform(get("/lobby/XXXXX/join").param("username", "TestUser")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Could not find lobby.")); } @Test void leaveLobbyShouldReturn200WhenSuccess() throws Exception { when(service.getIsStarted("ABCDE")).thenReturn(false); - when(service.leaveLobby("TestUser", "ABCDE")).thenReturn(true); + when(service.leaveLobby(anyString(), anyString())).thenReturn(true); mockMvc - .perform(get("/lobby/ABCDE/leave").param("username", "TestUser")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Left lobby successfully.")); + .perform(get("/lobby/ABCDE/leave") + .param("username", "TestUser") + .param("uuid", "mock-uuid-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Left lobby successfully.")); } @Test @@ -109,10 +126,12 @@ void leaveLobbyNoSuccess() throws Exception { when(service.leaveLobby("TestUser", "ABCDE")).thenReturn(false); mockMvc - .perform( - get("/lobby/ABCDE/leave").param("username", "TestUser").param("lobbyCode", "ABCDE")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value("Could not find lobby.")); + .perform(get("/lobby/ABCDE/leave") + .param("username", "TestUser") + .param("lobbyCode", "ABCDE") + .param("uuid", "mock-uuid-123")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Could not find lobby.")); } @Test @@ -179,36 +198,40 @@ void getLobbyInfoShouldReturn200WhenLobbyExists() throws Exception { @Test void testStartGameReturns200WhenConditionIsMet() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true, null), - new PlayerDto("Bob", null, null, false, null)); + List.of(new PlayerDto("Alice", null, null, true, null), + new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); - when(service.startGame("ABCDE", "Alice")).thenReturn(true); + when(service.startGame(anyString(), anyString())).thenReturn(true); mockMvc - .perform(get("/lobby/ABCDE/start-game").param("username", "Alice")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Game is starting now.")) - .andExpect(jsonPath("$.lobbyCode").value("ABCDE")) - .andExpect(jsonPath("$.playerList[0].username").value("Alice")) - .andExpect(jsonPath("$.isStarted").value("true")); + .perform(get("/lobby/ABCDE/start-game") + .param("username", "Alice") + .param("uuid", "mock-uuid-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Game is starting now.")) + .andExpect(jsonPath("$.lobbyCode").value("ABCDE")) + .andExpect(jsonPath("$.playerList[0].username").value("Alice")) + .andExpect(jsonPath("$.isStarted").value("true")); } @Test void testStartGameReturns400WhenServiceReturnsFalse() throws Exception { List players = - List.of(new PlayerDto("Alice", null, null, true, null), - new PlayerDto("Bob", null, null, false, null)); + List.of(new PlayerDto("Alice", null, null, true, null), + new PlayerDto("Bob", null, null, false, null)); when(service.getPlayersDto("ABCDE")).thenReturn(players); when(service.startGame("ABCDE", "Alice")).thenReturn(false); mockMvc - .perform(get("/lobby/ABCDE/start-game").param("username", "Alice")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value("Could not start the game.")) - .andExpect(jsonPath("$.lobbyCode").value("ABCDE")) - .andExpect(jsonPath("$.playerList[0].username").value("Alice")) - .andExpect(jsonPath("$.isStarted").value("false")); + .perform(get("/lobby/ABCDE/start-game") + .param("username", "Alice") + .param("uuid", "mock-uuid-123")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Could not start the game.")) + .andExpect(jsonPath("$.lobbyCode").value("ABCDE")) + .andExpect(jsonPath("$.playerList[0].username").value("Alice")) + .andExpect(jsonPath("$.isStarted").value("false")); } } diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index c887ce52..0a768b98 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -322,7 +322,9 @@ void getPlayersDtoShouldReturnEmptyList_whenLobbyDoesNotExist() { @Test void testAddGameManagerForLobby() { lobbyService.createLobby("User"); - lobbyService.startGame("ABCDE", "User"); + String hostUuid = lobbyService.getHost("ABCDE"); + lobbyService.startGame("ABCDE", hostUuid); + verify(gameService, times(1)).createGameManager(eq("ABCDE"), any(Team.class)); } @@ -348,10 +350,15 @@ void testGetHostWorks() { lobbyService.joinLobby("Bob", "ABCDE", null); lobbyService.joinLobby("Caesar", "ABCDE", null); - String expected = "Alice"; + String expectedUuid = lobbyService.getPlayers("ABCDE").stream() + .filter(Player::isHost) + .findFirst() + .orElseThrow() + .uuid(); + String result = lobbyService.getHost("ABCDE"); - assertEquals(expected, result); + assertEquals(expectedUuid, result); } @ParameterizedTest From 6810356d6045e6e8ba37ebe2fa95b98f1889aebc Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 21:35:29 +0200 Subject: [PATCH 162/172] fix: same username can be picked multiple times --- .../codenames/backend/lobby/domain/Lobby.java | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java index 36e54289..bb466e73 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/domain/Lobby.java @@ -25,10 +25,10 @@ public class Lobby { private final List playerList = new CopyOnWriteArrayList<>(); private final SecureRandom random = new SecureRandom(); - /** Maps a username to the selected team. */ + /** Maps a UUID to the selected team. */ private final Map playerTeams; - /** Maps a username to the selected role. */ + /** Maps a UUID to the selected role. */ private final Map playerRoles; /** @@ -44,6 +44,28 @@ public Lobby(String lobbyCode, String username) { this.addPlayer(username, true); } + /** + * Creates an empty lobby. Primarily used during the database restoration process. + * + * @param lobbyCode the unique code identifying the lobby + */ + + public Lobby(String lobbyCode) { + this.lobbyCode = lobbyCode; + this.playerTeams = new HashMap<>(); + this.playerRoles = new HashMap<>(); + } + + /** + * Directly adds a pre-existing player object back into the lobby. + * + * @param player the player to restore + */ + + public void addRestoredPlayer(Player player) { + playerList.add(player); + } + /** * Adds a new player to the lobby or reconnects an existing player. * @@ -51,21 +73,20 @@ public Lobby(String lobbyCode, String username) { * @param isHost whether the player is the host of the lobby * @param requestedUuid the UUID provided by the client to verify a reconnect * @return {@link Player} if the player was added or reconnected, {@code null} otherwise + *{@code null} if the lobby is full or if the reconnect UUID was not found. */ public Player addPlayer(String username, boolean isHost, String requestedUuid) { - Optional existingPlayerOpt = playerList.stream() - .filter(p -> p.username().equals(username)) - .findFirst(); - - if (existingPlayerOpt.isPresent()) { - Player existingPlayer = existingPlayerOpt.get(); - - if (existingPlayer.uuid().equals(requestedUuid)) { - return existingPlayer; + if (requestedUuid != null) { + Optional existingPlayerOpt = playerList.stream() + .filter(p -> p.uuid().equals(requestedUuid)) + .findFirst(); + + if (existingPlayerOpt.isPresent()) { + return existingPlayerOpt.get(); + } else { + return null; } - - return null; } if (playerList.size() >= MAX_PLAYERS) { @@ -81,9 +102,9 @@ public Player addPlayer(String username, boolean isHost, String requestedUuid) { /** * Adds a player to lobby. * - * @param username der Name des Spielers - * @param isHost gibt an, ob der Spieler der Host ist - * @return der erstellte Player + * @param username the username of the player + * @param isHost indicates which player is the host + * @return the player or {@code null}, if lobby is full */ public Player addPlayer(String username, boolean isHost) { @@ -91,7 +112,7 @@ public Player addPlayer(String username, boolean isHost) { } /** - * Adds a player to the lobby if capacity allows and the username is unique. Calls {@link + * Adds a player to the lobby if capacity allows and the UUID is unique. Calls {@link * #addPlayer(String, boolean)} with {@code false} as the second argument * * @param username the username of the player @@ -104,79 +125,62 @@ public Player addPlayer(String username, String uuid) { /** * Removes a player from the lobby. * - * @param username the username of the player to remove + * @param uuid the uuid of the player to remove */ - public void removePlayer(String username) { - playerList.removeIf(p -> p.username().equals(username)); - this.playerTeams.remove(username); - this.playerRoles.remove(username); + public void removePlayer(String uuid) { + playerList.removeIf(p -> p.uuid().equals(uuid)); + this.playerTeams.remove(uuid); + this.playerRoles.remove(uuid); } /** * Checks whether a player with the given username is in the lobby. * - * @param username the username to check + * @param uuid the uuid to check * @return {@code true} if the player exists in the lobby, {@code false} otherwise */ - public boolean hasPlayer(String username) { - return playerList.stream().anyMatch(p -> p.username().equals(username)); + public boolean hasPlayer(String uuid) { + return playerList.stream().anyMatch(p -> p.uuid().equals(uuid)); } /** * Sets the team for a player. * - * @param username the username of the player + * @param uuid the UUID of the player * @param team the team to assign */ - public void setPlayerTeam(String username, Team team) { - playerTeams.put(username, team); + public void setPlayerTeam(String uuid, Team team) { + playerTeams.put(uuid, team); } /** * Sets the role for a player. * - * @param username the username of the player + * @param uuid the UUID of the player * @param role the role to assign */ - public void setPlayerRole(String username, Role role) { - playerRoles.put(username, role); + public void setPlayerRole(String uuid, Role role) { + playerRoles.put(uuid, role); } - /** - * Sets the UUID for a player by replacing their Player record, used by restoration of UUID. - * - * @param username the username of the player - * @param uuid the UUID to assign - */ - public void setPlayerUuid(String username, String uuid) { - for (int i = 0; i < playerList.size(); i++) { - Player p = playerList.get(i); - if (p.username().equals(username)) { - // caller loops through all players in list on match we overwrite - playerList.set(i, new Player(p.username(), p.isHost(), uuid)); - return; - } - } - } // refer RestorationMapper as for why we overwrite player records - /** * Returns the team of a player. * - * @param username the username of the player + * @param uuid the UUID of the player * @return the assigned team, or {@code null} if none is assigned */ - public Team getPlayerTeam(String username) { - return playerTeams.get(username); + public Team getPlayerTeam(String uuid) { + return playerTeams.get(uuid); } /** * Returns the role of a player. * - * @param username the username of the player + * @param uuid the UUID of the player * @return the assigned role, or {@code null} if none is assigned */ - public Role getPlayerRole(String username) { - return playerRoles.get(username); + public Role getPlayerRole(String uuid) { + return playerRoles.get(uuid); } /** From dbc4730a0bcf3150984d467770755d8e6e83b81d Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 21:35:53 +0200 Subject: [PATCH 163/172] refactor: change files so uuid is used instead of username --- .../database/repository/PlayerRepository.java | 4 +-- .../restoration/RestorationMapper.java | 35 ++++++------------- .../backend/lobby/api/LobbyController.java | 10 +++--- .../shared/websocket/SessionRegistry.java | 8 ++--- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java index 26056bb2..9dd4301a 100644 --- a/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java +++ b/src/main/java/com/codenames/codenames/backend/database/repository/PlayerRepository.java @@ -15,10 +15,10 @@ public interface PlayerRepository extends JpaRepository { * Custom SQL query that is not included in the JpaRepository interface. * * @param lobbyCode the 5 character lobby code we want to search the player in - * @param username the username of the player we want to search for + * @param uuid the UUID of the player we want to search for * @return the player */ // First Lobby is the Lobby object inside the entity where we can find the lobby_code String. // And "resets" the SQL search back to the root table Player - Optional findByLobbyEntityLobbyCodeAndUsername(String lobbyCode, String username); + Optional findByLobbyEntityLobbyCodeAndUuid(String lobbyCode, String uuid); } diff --git a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java index 2addf2e9..e6e99b45 100644 --- a/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java +++ b/src/main/java/com/codenames/codenames/backend/database/restoration/RestorationMapper.java @@ -10,6 +10,7 @@ import com.codenames.codenames.backend.game.domain.Color; import com.codenames.codenames.backend.lobby.api.dto.PlayerDto; import com.codenames.codenames.backend.lobby.domain.Lobby; +import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; import java.util.ArrayList; @@ -100,39 +101,25 @@ private List mapToPlayerDto(LobbyEntity lobbyEntity) { } private Lobby buildLobby(String lobbyCode, List players) { - String hostUsername = findHostUsername(players); - // the way uuid is generated is a bit roundabout, but I do not have the time to refactor it - // and I do not want to touch the code if it works (Lobby.java) - // since constructor uses the two parameter method -> reroute to 3 parameter -> always null UUID - Lobby lobby = new Lobby(lobbyCode, hostUsername); + Lobby lobby = new Lobby(lobbyCode); for (PlayerDto player : players) { - if (!player.username().equals(hostUsername)) { - lobby.addPlayer(player.username(), player.isHost()); - } + lobby.addRestoredPlayer( + new Player( + player.username(), + player.isHost(), + player.uuid())); + if (player.team() != null) { - lobby.setPlayerTeam(player.username(), player.team()); + lobby.setPlayerTeam(player.uuid(), player.team()); } + if (player.role() != null) { - lobby.setPlayerRole(player.username(), player.role()); - } - if (player.uuid() != null) { - // Solution: we overwrite all player records with data loaded from DB - lobby.setPlayerUuid(player.username(), player.uuid()); + lobby.setPlayerRole(player.uuid(), player.role()); } } return lobby; } - - private static String findHostUsername(List players) { - String hostUsername = ""; - for (PlayerDto playerDto : players) { - if (playerDto.isHost()) { - hostUsername = playerDto.username(); - } - } - return hostUsername; - } } diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index fc41a141..3c18cddc 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -40,12 +40,12 @@ public LobbyController(LobbyService service) { /** * Handles a request to create a new lobby. * - * @param username the username of the requesting user + * @param uuid the UUID of the requesting user * @return a response containing the result and the generated lobby code */ @GetMapping("/create") - public ResponseEntity createLobby(@RequestParam String username) { - String lobbyCode = service.createLobby(username); + public ResponseEntity createLobby(@RequestParam String uuid) { + String lobbyCode = service.createLobby(uuid); if (lobbyCode == null || lobbyCode.isBlank()) { return ResponseEntity.internalServerError() .body(new LobbyResponse("Error while creating lobby.", "", null, false, null)); @@ -53,7 +53,7 @@ public ResponseEntity createLobby(@RequestParam String username) List players = service.getPlayersDto(lobbyCode); Player host = Objects.requireNonNull( - service.getPlayer(lobbyCode, username)); + service.getPlayer(lobbyCode, uuid)); String hostUuid = host.uuid(); @@ -153,7 +153,7 @@ public ResponseEntity getLobbyInfo(@PathVariable String lobbyCode public ResponseEntity selectPosition( @PathVariable String lobbyCode, @RequestBody PlayerDto request) { boolean updated = - service.selectPosition(request.username(), lobbyCode, request.team(), request.role()); + service.selectPosition(request.uuid(), lobbyCode, request.team(), request.role()); if (updated) { return ResponseEntity.ok( diff --git a/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java b/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java index fb36b2fe..33715ff7 100644 --- a/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java +++ b/src/main/java/com/codenames/codenames/backend/shared/websocket/SessionRegistry.java @@ -7,7 +7,7 @@ /** * Registry for managing WebSocket sessions and their associated users and lobbies. * - *

Maintains mappings between session IDs, usernames, and lobby codes to support messaging and + *

Maintains mappings between session IDs, UUID's, and lobby codes to support messaging and * cleanup on disconnect. * *

This implementation is thread-safe. @@ -21,12 +21,12 @@ public class SessionRegistry { * Registers a session with its associated user and lobby. * * @param sessionId the WebSocket session ID - * @param username the username of the player + * @param uuid the UUID of the player * @param lobbyCode the lobby code */ - public void register(String sessionId, String username, String lobbyCode) { + public void register(String sessionId, String uuid, String lobbyCode) { sessionToLobby.put(sessionId, lobbyCode); - sessionToUser.put(sessionId, username); + sessionToUser.put(sessionId, uuid); } /** From e370f2ab59f7b5d93ed35451b3ebba84179e04e0 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 21:36:11 +0200 Subject: [PATCH 164/172] test: change test to align with new changes --- .../lobby/application/LobbyService.java | 53 +++-- .../lobby/api/LobbyControllerTest.java | 88 +++++---- .../lobby/application/LobbyServiceTest.java | 185 ++++++++++-------- .../backend/lobby/domain/LobbyTest.java | 118 ++++++----- 4 files changed, 233 insertions(+), 211 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index a59b2cbc..4c67baad 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -91,12 +91,11 @@ public Player joinLobby(String username, String lobbyCode, String uuid) { if (lobby != null) { if (gameService.isGameStarted(lobbyCode)) { - Player existingPlayer = lobby.getPlayerList().stream() - .filter(p -> p.username().equals(username)) - .findFirst() - .orElse(null); + boolean validReconnect = + lobby.getPlayerList().stream() + .anyMatch(p -> p.uuid().equals(uuid)); - if (existingPlayer == null || !existingPlayer.uuid().equals(uuid)) { + if (!validReconnect) { log.warn("{}: join rejected. Game started, no valid reconnect UUID.", lobbyCode); return null; } @@ -143,27 +142,27 @@ public boolean leaveLobby(String uuid, String lobbyCode) { /** * Assigns a team and role to a player in a lobby. * - * @param username the username of the player + * @param uuid the UUID of the player * @param lobbyCode the lobby code identifying the lobby * @param team the selected team * @param role the selected role * @return {@code true} if the position was assigned, {@code false} otherwise */ - public boolean selectPosition(String username, String lobbyCode, Team team, Role role) { + public boolean selectPosition(String uuid, String lobbyCode, Team team, Role role) { Lobby lobby = lobbyList.get(lobbyCode); - if (lobby == null || !lobby.hasPlayer(username) || team == null || role == null) { + if (lobby == null || !lobby.hasPlayer(uuid) || team == null || role == null) { log.error("{}: position selection error occurred", lobbyCode); return false; } - if (role == Role.SPYMASTER && isSpymasterAlreadyAssigned(lobby, username, team)) { + if (role == Role.SPYMASTER && isSpymasterAlreadyAssigned(lobby, uuid, team)) { log.error("{}: position selection error occurred, spymaster is already assigned.", lobbyCode); return false; } - lobby.setPlayerTeam(username, team); - lobby.setPlayerRole(username, role); + lobby.setPlayerTeam(uuid, team); + lobby.setPlayerRole(uuid, role); log.info("{}: new role was assigned to player", lobbyCode); return true; } @@ -210,8 +209,8 @@ public List getPlayersDto(String lobbyCode) { player -> new PlayerDto( player.username(), - lobby.getPlayerTeam(player.username()), - lobby.getPlayerRole(player.username()), + lobby.getPlayerTeam(player.uuid()), + lobby.getPlayerRole(player.uuid()), player.isHost(), player.uuid())) .toList(); @@ -223,15 +222,15 @@ public List getPlayersDto(String lobbyCode) { * Checks whether a spymaster is already assigned for the given team in the lobby. * * @param lobby the lobby to inspect - * @param username the username requesting the role + * @param uuid the UUID requesting the role * @param team the team to inspect * @return {@code true} if a different player is already the spymaster for that team */ - private boolean isSpymasterAlreadyAssigned(Lobby lobby, String username, Team team) { + private boolean isSpymasterAlreadyAssigned(Lobby lobby, String uuid, Team team) { for (Player player : lobby.getPlayerList()) { - if (!player.username().equals(username) - && lobby.getPlayerTeam(player.username()) == team - && lobby.getPlayerRole(player.username()) == Role.SPYMASTER) { + if (!player.uuid().equals(uuid) + && lobby.getPlayerTeam(player.uuid()) == team + && lobby.getPlayerRole(player.uuid()) == Role.SPYMASTER) { return true; } } @@ -262,14 +261,14 @@ private String generateLobbyCode() { /** * Retrieves the team of a player in a lobby. * - * @param username the username of a player + * @param uuid the UUID of a player * @param lobbyCode the lobby code of the lobby * @return the team of the player, or {@code null} if the lobby or player does not exist */ - public Team getPlayerTeam(String username, String lobbyCode) { + public Team getPlayerTeam(String uuid, String lobbyCode) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { - return lobby.getPlayerTeam(username); + return lobby.getPlayerTeam(uuid); } return null; } @@ -277,14 +276,14 @@ public Team getPlayerTeam(String username, String lobbyCode) { /** * Retrieves the role of a player in a lobby. * - * @param username the username of a player + * @param uuid the UUID of a player * @param lobbyCode the lobby code of the lobby * @return the role of the player, or {@code null} if the lobby or player does not exist */ - public Role getPlayerRole(String username, String lobbyCode) { + public Role getPlayerRole(String uuid, String lobbyCode) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { - return lobby.getPlayerRole(username); + return lobby.getPlayerRole(uuid); } return null; } @@ -293,14 +292,14 @@ public Role getPlayerRole(String username, String lobbyCode) { * Retrieves a specific player from a lobby. * * @param lobbyCode the lobby code - * @param username the username of the player + * @param uuid the UUID of the player * @return the Player object, or null if not found */ - public Player getPlayer(String lobbyCode, String username) { + public Player getPlayer(String lobbyCode, String uuid) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { return lobby.getPlayerList().stream() - .filter(p -> p.username().equals(username)) + .filter(p -> p.uuid().equals(uuid)) .findFirst() .orElse(null); } diff --git a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java index 297f5b4f..0aae9a10 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/api/LobbyControllerTest.java @@ -12,15 +12,15 @@ import com.codenames.codenames.backend.lobby.domain.Player; import com.codenames.codenames.backend.lobby.domain.Role; import com.codenames.codenames.backend.lobby.domain.Team; +import com.codenames.codenames.backend.shared.config.SecurityConfig; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import com.codenames.codenames.backend.shared.config.SecurityConfig; -import org.springframework.context.annotation.Import; @WebMvcTest(LobbyController.class) @Import(SecurityConfig.class) @@ -31,41 +31,47 @@ class LobbyControllerTest { @MockBean private LobbyService service; @Test - void createLobbyShouldReturn200() throws Exception { - when(service.createLobby("TestUser")).thenReturn("ABCDE"); - when(service.getPlayer("ABCDE", - "TestUser")).thenReturn(new Player("TestUser", - true, - "mock-uuid") - ); + void createLobbyShouldReturn200() throws Exception { + when(service.createLobby(anyString())).thenReturn("ABCDE"); + when(service.getPlayer(anyString(), anyString())) + .thenReturn(new Player("TestUser", true, "mock-uuid-123")); mockMvc - .perform(get("/lobby/create").param("username", "TestUser")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Successfully created Lobby.")) - .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); + .perform( + get("/lobby/create") + .param("username", "TestUser") + .param("uuid", "mock-uuid-123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Successfully created Lobby.")) + .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); } @Test - void createLobbyBlankLobbyCode() throws Exception { + void createLobbyBlankLobbyCode() throws Exception { when(service.createLobby("TestUser")).thenReturn(""); mockMvc - .perform(get("/lobby/create").param("username", "TestUser")) - .andExpect(status().isInternalServerError()) - .andExpect(jsonPath("$.message").value("Error while creating lobby.")) - .andExpect(jsonPath("$.lobbyCode").value("")); + .perform( + get("/lobby/create") + .param("username", "TestUser") + .param("uuid", "mock-uuid-123")) // <-- Hier fehlte die UUID + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.message").value("Error while creating lobby.")) + .andExpect(jsonPath("$.lobbyCode").value("")); } @Test - void createLobbyNullLobbyCode() throws Exception { + void createLobbyNullLobbyCode() throws Exception { when(service.createLobby("TestUser")).thenReturn(null); mockMvc - .perform(get("/lobby/create").param("username", "TestUser")) - .andExpect(status().isInternalServerError()) - .andExpect(jsonPath("$.message").value("Error while creating lobby.")) - .andExpect(jsonPath("$.lobbyCode").value("")); + .perform( + get("/lobby/create") + .param("username", "TestUser") + .param("uuid", "mock-uuid-123")) // <-- Hier fehlte die UUID + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.message").value("Error while creating lobby.")) + .andExpect(jsonPath("$.lobbyCode").value("")); } @Test @@ -138,25 +144,27 @@ void leaveLobbyNoSuccess() throws Exception { } @Test - void selectPositionShouldReturn200whenSuccess() throws Exception { - when(service.selectPosition("TestUser", "ABCDE", Team.RED, Role.SPYMASTER)).thenReturn(true); + void selectPositionShouldReturn200whenSuccess() throws Exception { + when(service.selectPosition("mock-uuid-123", "ABCDE", Team.RED, Role.SPYMASTER)) + .thenReturn(true); mockMvc - .perform( - post("/lobby/ABCDE/select-position") - .contentType(MediaType.APPLICATION_JSON) - .content( - """ - { - "username": "TestUser", - "team": "RED", - "role": "SPYMASTER", - "isHost": "true" - } - """)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.message").value("Position selected successfully.")) - .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); + .perform( + post("/lobby/ABCDE/select-position") + .contentType(MediaType.APPLICATION_JSON) + .content( + """ + { + "username": "TestUser", + "uuid": "mock-uuid-123", + "team": "RED", + "role": "SPYMASTER", + "isHost": true + } + """)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Position selected successfully.")) + .andExpect(jsonPath("$.lobbyCode").value("ABCDE")); } @Test diff --git a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java index 0a768b98..2652fba8 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/application/LobbyServiceTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -28,11 +29,6 @@ import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; -/** - * Tests for {@link LobbyService}. - * - *

Validates lobby creation, joining, leaving, and player management behavior. - */ class LobbyServiceTest { private LobbyService lobbyService; @@ -41,7 +37,7 @@ class LobbyServiceTest { private LobbyRepository lobbyRepository; @BeforeEach - void setup() { + void setup() { generator = mock(LobbyCodeGenerator.class); gameService = mock(GameService.class); ChatService chatService = mock(ChatService.class); @@ -52,7 +48,7 @@ void setup() { } @Test - void createLobbyReturnLobbyCode() { + void createLobbyReturnLobbyCode() { lobbyService.createLobby("Host"); Player result = lobbyService.joinLobby("TestUser", "ABCDE", null); @@ -63,7 +59,7 @@ void createLobbyReturnLobbyCode() { } @Test - void createLobbyLobbyCodeIsNull() { + void createLobbyLobbyCodeIsNull() { when(generator.generateLobbyCode()).thenReturn(null); String result = lobbyService.createLobby("Host"); @@ -71,7 +67,7 @@ void createLobbyLobbyCodeIsNull() { } @Test - void createLobbyLobbyCodeIsBlank() { + void createLobbyLobbyCodeIsBlank() { when(generator.generateLobbyCode()).thenReturn(""); String result = lobbyService.createLobby("Host"); @@ -79,32 +75,33 @@ void createLobbyLobbyCodeIsBlank() { } @Test - void joinLobbyReturnFalseLobbyNotExists() { + void joinLobbyReturnFalseLobbyNotExists() { Player result = lobbyService.joinLobby("TestUser", "ABCDE", null); assertNull(result); } @Test - void leaveLobbyReturnTrueLobbyExists() { + void leaveLobbyReturnTrueLobbyExists() { lobbyService.createLobby("Host"); + String hostUuid = lobbyService.getHost("ABCDE"); - boolean result = lobbyService.leaveLobby("Host", "ABCDE"); + boolean result = lobbyService.leaveLobby(hostUuid, "ABCDE"); assertTrue(result); List players = lobbyService.getPlayers("ABCDE"); - assertFalse(players.stream().anyMatch(p -> p.username().equals("Host"))); + assertFalse(players.stream().anyMatch(p -> p.uuid().equals(hostUuid))); } @Test - void leaveLobbyReturnFalseLobbyNotExists() { - boolean result = lobbyService.leaveLobby("Host", "ABCDE"); + void leaveLobbyReturnFalseLobbyNotExists() { + boolean result = lobbyService.leaveLobby("wrong-uuid", "ABCDE"); assertFalse(result); } @Test - void createLobbyShouldGenerateNewCodeIfDuplicateExists() { + void createLobbyShouldGenerateNewCodeIfDuplicateExists() { when(generator.generateLobbyCode()).thenReturn("ABCDE").thenReturn("ABCDE").thenReturn("FGHIJ"); lobbyService.createLobby("Host1"); @@ -114,68 +111,78 @@ void createLobbyShouldGenerateNewCodeIfDuplicateExists() { } @Test - void selectPositionShouldReturnTrueWhenPlayerChoosesTeamAndRole() { + void selectPositionShouldReturnTrueWhenPlayerChoosesTeamAndRole() { lobbyService.createLobby("Host"); + String hostUuid = lobbyService.getHost("ABCDE"); - boolean result = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); + boolean result = lobbyService.selectPosition(hostUuid, "ABCDE", Team.RED, Role.SPYMASTER); assertTrue(result); } @Test - void selectPositionShouldReturnFalseWhenLobbyDoesNotExist() { - boolean result = lobbyService.selectPosition("Host", "XXXXX", Team.RED, Role.SPYMASTER); + void selectPositionShouldReturnFalseWhenLobbyDoesNotExist() { + boolean result = lobbyService.selectPosition("wrong-uuid", "XXXXX", Team.RED, Role.SPYMASTER); assertFalse(result); } @Test - void selectPositionShouldReturnFalseWhenPlayerIsNotInLobby() { + void selectPositionShouldReturnFalseWhenPlayerIsNotInLobby() { lobbyService.createLobby("Host"); - boolean result = lobbyService.selectPosition("Ghost", "ABCDE", Team.RED, Role.SPYMASTER); + boolean result = lobbyService.selectPosition("wrong-uuid", "ABCDE", Team.RED, Role.SPYMASTER); assertFalse(result); } @Test - void selectPositionShouldReturnFalseWhenSecondSpymasterChoosesSameTeam() { + void selectPositionShouldReturnFalseWhenSecondSpymasterChoosesSameTeam() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE", null); + Player p1 = lobbyService.joinLobby("P1", "ABCDE", null); + String hostUuid = lobbyService.getHost("ABCDE"); - boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); - boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.RED, Role.SPYMASTER); + boolean firstResult = lobbyService.selectPosition(hostUuid, "ABCDE", + Team.RED, Role.SPYMASTER); + boolean secondResult = lobbyService.selectPosition(p1.uuid(), "ABCDE", + Team.RED, Role.SPYMASTER); assertTrue(firstResult); assertFalse(secondResult); } @Test - void selectPositionShouldReturnTrueWhenSpymastersChooseDifferentTeams() { + void selectPositionShouldReturnTrueWhenSpymastersChooseDifferentTeams() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE", null); + Player p1 = lobbyService.joinLobby("P1", "ABCDE", null); + String hostUuid = lobbyService.getHost("ABCDE"); - boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); - boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.BLUE, Role.SPYMASTER); + boolean firstResult = lobbyService.selectPosition(hostUuid, "ABCDE", + Team.RED, Role.SPYMASTER); + boolean secondResult = lobbyService.selectPosition(p1.uuid(), "ABCDE", + Team.BLUE, Role.SPYMASTER); assertTrue(firstResult); assertTrue(secondResult); } @Test - void selectPositionShouldReturnTrueWhenMultipleOperativesChooseSameTeam() { + void selectPositionShouldReturnTrueWhenMultipleOperativesChooseSameTeam() { lobbyService.createLobby("Host"); - lobbyService.joinLobby("P1", "ABCDE", null); + Player p1 = lobbyService.joinLobby("P1", "ABCDE", null); + String hostUuid = lobbyService.getHost("ABCDE"); - boolean firstResult = lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.OPERATIVE); - boolean secondResult = lobbyService.selectPosition("P1", "ABCDE", Team.RED, Role.OPERATIVE); + boolean firstResult = lobbyService.selectPosition(hostUuid, "ABCDE", + Team.RED, Role.OPERATIVE); + boolean secondResult = lobbyService.selectPosition(p1.uuid(), "ABCDE", + Team.RED, Role.OPERATIVE); assertTrue(firstResult); assertTrue(secondResult); } @Test - void getPlayersShouldReturnEmptyListWhenLobbyDoesNotExist() { + void getPlayersShouldReturnEmptyListWhenLobbyDoesNotExist() { List players = lobbyService.getPlayers("UNKNOWN"); assertNotNull(players); @@ -183,119 +190,130 @@ void getPlayersShouldReturnEmptyListWhenLobbyDoesNotExist() { } @Test - void joinLobbyShouldReturnFalseWhenPlayerAlreadyExists() { + void joinLobbyShouldAllowDuplicateUsernamesWithDifferentUuids() { lobbyService.createLobby("Host"); Player first = lobbyService.joinLobby("Max", "ABCDE", null); Player second = lobbyService.joinLobby("Max", "ABCDE", null); assertNotNull(first); - assertNull(second); + assertNotNull(second); + assertNotEquals(first.uuid(), second.uuid()); List players = lobbyService.getPlayers("ABCDE"); long count = players.stream().filter(p -> p.username().equals("Max")).count(); - assertEquals(1, count); + assertEquals(2, count); } @Test - void selectPositionShouldReturnFalseIfTeamIsNull() { + void selectPositionShouldReturnFalseIfTeamIsNull() { when(generator.generateLobbyCode()).thenReturn("ABCDE"); String lobbyCode = lobbyService.createLobby("Host"); + String hostUuid = lobbyService.getHost(lobbyCode); - boolean result = lobbyService.selectPosition("Host", lobbyCode, null, Role.OPERATIVE); + boolean result = lobbyService.selectPosition(hostUuid, lobbyCode, null, Role.OPERATIVE); assertFalse(result); } @Test - void selectPositionShouldReturnFalseIfRoleIsNull() { + void selectPositionShouldReturnFalseIfRoleIsNull() { when(generator.generateLobbyCode()).thenReturn("ABCDE"); String lobbyCode = lobbyService.createLobby("Host"); + String hostUuid = lobbyService.getHost(lobbyCode); - boolean result = lobbyService.selectPosition("Host", lobbyCode, Team.RED, null); + boolean result = lobbyService.selectPosition(hostUuid, lobbyCode, Team.RED, null); assertFalse(result); } @Test - void testGetPlayerTeam() { + void testGetPlayerTeam() { lobbyService.createLobby("Host"); - lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); + String hostUuid = lobbyService.getHost("ABCDE"); + lobbyService.selectPosition(hostUuid, "ABCDE", Team.RED, Role.SPYMASTER); - assertEquals(Team.RED, lobbyService.getPlayerTeam("Host", "ABCDE")); + assertEquals(Team.RED, lobbyService.getPlayerTeam(hostUuid, "ABCDE")); } @Test - void getPlayerTeam_wrongCode() { - assertNull(lobbyService.getPlayerTeam("Host", "invalidCode")); + void getPlayerTeam_wrongCode() { + assertNull(lobbyService.getPlayerTeam("wrong-uuid", "invalidCode")); } @Test - void getPlayerTeam_nonExistentPlayer() { + void getPlayerTeam_nonExistentPlayer() { String lobbyCode = lobbyService.createLobby("Host"); - assertNull(lobbyService.getPlayerTeam("nonExistentPlayer", lobbyCode)); + assertNull(lobbyService.getPlayerTeam("wrong-uuid", lobbyCode)); } @Test - void testGetPlayerRole() { + void testGetPlayerRole() { lobbyService.createLobby("Host"); - lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.OPERATIVE); + String hostUuid = lobbyService.getHost("ABCDE"); + lobbyService.selectPosition(hostUuid, "ABCDE", Team.RED, Role.OPERATIVE); - assertEquals(Role.OPERATIVE, lobbyService.getPlayerRole("Host", "ABCDE")); + assertEquals(Role.OPERATIVE, lobbyService.getPlayerRole(hostUuid, "ABCDE")); } @Test - void getPlayerRole_wrongCode() { - assertNull(lobbyService.getPlayerRole("Host", "test")); + void getPlayerRole_wrongCode() { + assertNull(lobbyService.getPlayerRole("wrong-uuid", "test")); } @Test - void getPlayerRole_nonExistentPlayer() { + void getPlayerRole_nonExistentPlayer() { String lobbyCode = lobbyService.createLobby("Host"); - assertNull(lobbyService.getPlayerRole("nonExistentPlayer", lobbyCode)); + assertNull(lobbyService.getPlayerRole("wrong-uuid", lobbyCode)); } @Test - void testLobbyIsRemovedWhenItIsEmpty() { + void testLobbyIsRemovedWhenItIsEmpty() { lobbyService.createLobby("Host"); - lobbyService.leaveLobby("Host", "ABCDE"); + String hostUuid = lobbyService.getHost("ABCDE"); + + lobbyService.leaveLobby(hostUuid, "ABCDE"); lobbyService.checkLobbyStillHasPlayers("ABCDE"); + assertFalse(lobbyService.getLobbyList().containsKey("ABCDE")); verify(gameService, times(1)).removeGame("ABCDE"); verify(lobbyRepository, times(1)).deleteById("ABCDE"); } @Test - void testLobbyIsNotRemovedWhenItHasPlayers() { + void testLobbyIsNotRemovedWhenItHasPlayers() { lobbyService.createLobby("Host"); lobbyService.joinLobby("Player1", "ABCDE", null); lobbyService.checkLobbyStillHasPlayers("ABCDE"); + assertTrue(lobbyService.getLobbyList().containsKey("ABCDE")); } @Test - void testGetPlayersDto() { + void testGetPlayersDto() { lobbyService.createLobby("Host"); List players = lobbyService.getPlayersDto("ABCDE"); PlayerDto player = players.get(0); + assertEquals("Host", player.username()); assertTrue(player.isHost()); } @Test - void testGetPlayersDto_lobbyNotExists() { + void testGetPlayersDto_lobbyNotExists() { List players = lobbyService.getPlayersDto("UNKNOWN"); + assertNotNull(players); assertTrue(players.isEmpty()); } @Test - void getPlayersDtoShouldReturnPlayerDtosWhenLobbyExists() { + void getPlayersDtoShouldReturnPlayerDtosWhenLobbyExists() { lobbyService.createLobby("Host"); List result = lobbyService.getPlayersDto("ABCDE"); @@ -312,7 +330,7 @@ void getPlayersDtoShouldReturnPlayerDtosWhenLobbyExists() { } @Test - void getPlayersDtoShouldReturnEmptyList_whenLobbyDoesNotExist() { + void getPlayersDtoShouldReturnEmptyList_whenLobbyDoesNotExist() { List result = lobbyService.getPlayersDto("ABCDE"); assertNotNull(result); @@ -320,7 +338,7 @@ void getPlayersDtoShouldReturnEmptyList_whenLobbyDoesNotExist() { } @Test - void testAddGameManagerForLobby() { + void testAddGameManagerForLobby() { lobbyService.createLobby("User"); String hostUuid = lobbyService.getHost("ABCDE"); lobbyService.startGame("ABCDE", hostUuid); @@ -329,32 +347,34 @@ void testAddGameManagerForLobby() { } @Test - void testGetIsStarted() { + void testGetIsStarted() { when(gameService.isGameStarted("ABCDE")).thenReturn(true); boolean result = lobbyService.getIsStarted("ABCDE"); + assertTrue(result); } @Test - void testGetIsStartedGameServiceReturnsFalse() { + void testGetIsStartedGameServiceReturnsFalse() { when(gameService.isGameStarted("ABCDE")).thenReturn(false); boolean result = lobbyService.getIsStarted("ABCDE"); + assertFalse(result); } @Test - void testGetHostWorks() { + void testGetHostWorks() { lobbyService.createLobby("Alice"); lobbyService.joinLobby("Bob", "ABCDE", null); lobbyService.joinLobby("Caesar", "ABCDE", null); String expectedUuid = lobbyService.getPlayers("ABCDE").stream() - .filter(Player::isHost) - .findFirst() - .orElseThrow() - .uuid(); + .filter(Player::isHost) + .findFirst() + .orElseThrow() + .uuid(); String result = lobbyService.getHost("ABCDE"); @@ -364,14 +384,14 @@ void testGetHostWorks() { @ParameterizedTest @NullAndEmptySource @ValueSource(strings = {"ABCDE"}) - void testGetHostReturnsEmptyString(String lobbyCode) { + void testGetHostReturnsEmptyString(String lobbyCode) { String result = lobbyService.getHost(lobbyCode); assertEquals("", result); } @Test - void testRestoreLobbyAddsLobbyToLobbyList() { + void testRestoreLobbyAddsLobbyToLobbyList() { Lobby restoredLobby = new Lobby("ABCDE", "Host"); lobbyService.restoreLobby("ABCDE", restoredLobby); @@ -381,21 +401,22 @@ void testRestoreLobbyAddsLobbyToLobbyList() { } @Test - void getLobbySnapshotsShouldReturnAllLobbyPlayerDtos() { + void getLobbySnapshotsShouldReturnAllLobbyPlayerDtos() { lobbyService.createLobby("Host"); lobbyService.joinLobby("Player", "ABCDE", null); - lobbyService.selectPosition("Host", "ABCDE", Team.RED, Role.SPYMASTER); + String hostUuid = lobbyService.getHost("ABCDE"); + + lobbyService.selectPosition(hostUuid, "ABCDE", Team.RED, Role.SPYMASTER); Map> snapshots = lobbyService.getLobbySnapshots(); assertTrue(snapshots.containsKey("ABCDE")); assertEquals(2, snapshots.get("ABCDE").size()); - PlayerDto host = - snapshots.get("ABCDE").stream() - .filter(player -> player.username().equals("Host")) - .findFirst() - .orElseThrow(); + PlayerDto host = snapshots.get("ABCDE").stream() + .filter(player -> player.username().equals("Host")) + .findFirst() + .orElseThrow(); assertEquals(Team.RED, host.team()); assertEquals(Role.SPYMASTER, host.role()); @@ -403,7 +424,7 @@ void getLobbySnapshotsShouldReturnAllLobbyPlayerDtos() { } @Test - void joinLobbyShouldBlockNewPlayerIfGameStarted() { + void joinLobbyShouldBlockNewPlayerIfGameStarted() { when(gameService.isGameStarted("ABCDE")).thenReturn(true); Player result = lobbyService.joinLobby("NewPlayer", "ABCDE", null); @@ -412,7 +433,7 @@ void joinLobbyShouldBlockNewPlayerIfGameStarted() { } @Test - void joinLobbyShouldAllowReconnectIfGameStarted() { + void joinLobbyShouldAllowReconnectIfGameStarted() { lobbyService.getLobbyList().clear(); String lobbyCode = lobbyService.createLobby("Host"); diff --git a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java index 61c6495c..9d097e91 100644 --- a/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java +++ b/src/test/java/com/codenames/codenames/backend/lobby/domain/LobbyTest.java @@ -9,125 +9,126 @@ import org.junit.jupiter.api.Test; -/** - * Tests for {@link Lobby}. - * - *

Validates player management and lobby constraints. - */ class LobbyTest { @Test - void constructorShouldInitializeLobbyCorrectly() { + void constructorShouldInitializeLobbyCorrectly() { Lobby lobby = new Lobby("ABCDE", "Host"); assertEquals("ABCDE", lobby.getLobbyCode()); assertEquals(1, lobby.getPlayerList().size()); assertTrue(lobby.getPlayerList().stream().anyMatch(p -> p.username().equals("Host"))); + assertNotNull(lobby.getPlayerList().get(0).uuid()); } @Test - void addPlayerShouldAddPlayer() { + void addPlayerShouldAddPlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1", null); + lobby.addPlayer("P1", false); assertEquals(2, lobby.getPlayerList().size()); assertTrue(lobby.getPlayerList().stream().anyMatch(p -> p.username().equals("P1"))); } @Test - void addPlayerShouldNotExceedMaxPlayers() { + void addPlayerShouldNotExceedMaxPlayers() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1", null); - lobby.addPlayer("P2", null); - lobby.addPlayer("P3", null); - lobby.addPlayer("P4", null); + lobby.addPlayer("P1", false); + lobby.addPlayer("P2", false); + lobby.addPlayer("P3", false); + Player rejectedPlayer = lobby.addPlayer("P4", false); assertEquals(4, lobby.getPlayerList().size()); + assertNull(rejectedPlayer); } @Test - void removePlayerShouldRemovePlayer() { + void removePlayerShouldRemovePlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.addPlayer("P1", null); - lobby.removePlayer("P1"); + Player p1 = lobby.addPlayer("P1", false); + String p1Uuid = p1.uuid(); + + lobby.removePlayer(p1Uuid); - assertFalse(lobby.getPlayerList().stream().anyMatch(p -> p.username().equals("P1"))); + assertFalse(lobby.getPlayerList().stream().anyMatch(p -> p.uuid().equals(p1Uuid))); } @Test - void removePlayerShouldDoNothingIfPlayerNotExists() { + void removePlayerShouldDoNothingIfPlayerNotExists() { Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.removePlayer("Ghost"); + lobby.removePlayer("invalid-uuid-1234"); assertEquals(1, lobby.getPlayerList().size()); } @Test - void addPlayerShouldNotAddDuplicatePlayer() { + void addPlayerShouldAllowDuplicateUsernamesWithDifferentUuids() { Lobby lobby = new Lobby("ABCDE", "Host"); - Player first = lobby.addPlayer("Max", null); - Player second = lobby.addPlayer("Max", null); + Player first = lobby.addPlayer("Max", false); + Player second = lobby.addPlayer("Max", false); assertNotNull(first); - assertNull(second); - - long count = lobby.getPlayerList().stream().filter(p -> p.username().equals("Max")).count(); - - assertEquals(1, count); + assertNotNull(second); + assertNotEquals(first.uuid(), second.uuid()); + assertEquals(3, lobby.getPlayerList().size()); } @Test - void hasPlayerShouldReturnTrueIfPlayerExists() { + void hasPlayerShouldReturnTrueIfPlayerExists() { Lobby lobby = new Lobby("ABCDE", "Host"); + String hostUuid = lobby.getPlayerList().get(0).uuid(); - assertTrue(lobby.hasPlayer("Host")); + assertTrue(lobby.hasPlayer(hostUuid)); } @Test - void hasPlayerShouldReturnFalseIfPlayerDoesNotExist() { + void hasPlayerShouldReturnFalseIfPlayerDoesNotExist() { Lobby lobby = new Lobby("ABCDE", "Host"); - assertFalse(lobby.hasPlayer("Ghost")); + assertFalse(lobby.hasPlayer("invalid-uuid")); } @Test - void setPlayerTeamShouldStoreSelectedTeam() { + void setPlayerTeamShouldStoreSelectedTeam() { Lobby lobby = new Lobby("ABCDE", "Host"); + String hostUuid = lobby.getPlayerList().get(0).uuid(); - lobby.setPlayerTeam("Host", Team.RED); + lobby.setPlayerTeam(hostUuid, Team.RED); - assertEquals(Team.RED, lobby.getPlayerTeam("Host")); + assertEquals(Team.RED, lobby.getPlayerTeam(hostUuid)); } @Test - void setPlayerRoleShouldStoreSelectedRole() { + void setPlayerRoleShouldStoreSelectedRole() { Lobby lobby = new Lobby("ABCDE", "Host"); + String hostUuid = lobby.getPlayerList().get(0).uuid(); - lobby.setPlayerRole("Host", Role.SPYMASTER); + lobby.setPlayerRole(hostUuid, Role.SPYMASTER); - assertEquals(Role.SPYMASTER, lobby.getPlayerRole("Host")); + assertEquals(Role.SPYMASTER, lobby.getPlayerRole(hostUuid)); } @Test - void removePlayerShouldAlsoRemoveStoredTeamAndRole() { + void removePlayerShouldAlsoRemoveStoredTeamAndRole() { Lobby lobby = new Lobby("ABCDE", "Host"); + String hostUuid = lobby.getPlayerList().get(0).uuid(); - lobby.setPlayerTeam("Host", Team.BLUE); - lobby.setPlayerRole("Host", Role.OPERATIVE); + lobby.setPlayerTeam(hostUuid, Team.BLUE); + lobby.setPlayerRole(hostUuid, Role.OPERATIVE); - lobby.removePlayer("Host"); + lobby.removePlayer(hostUuid); - assertNull(lobby.getPlayerTeam("Host")); - assertNull(lobby.getPlayerRole("Host")); + assertNull(lobby.getPlayerTeam(hostUuid)); + assertNull(lobby.getPlayerRole(hostUuid)); } @Test - void startingTeamShouldBeEitherRedOrBlue() { + void startingTeamShouldBeEitherRedOrBlue() { Lobby lobby = new Lobby("ABCDE", "Host"); Team startingTeam = lobby.decideStartingTeam(); @@ -136,7 +137,7 @@ void startingTeamShouldBeEitherRedOrBlue() { } @Test - void addPlayerShouldAssignUuidToNewPlayer() { + void addPlayerShouldAssignUuidToNewPlayer() { Lobby lobby = new Lobby("ABCDE", "Host"); Player newPlayer = lobby.addPlayer("P1", false); @@ -144,7 +145,7 @@ void addPlayerShouldAssignUuidToNewPlayer() { } @Test - void addPlayerShouldAllowReconnectWithCorrectUuid() { + void addPlayerShouldAllowReconnectWithCorrectUuid() { Lobby lobby = new Lobby("ABCDE", "Host"); Player first = lobby.addPlayer("P1", false); String validUuid = first.uuid(); @@ -156,7 +157,7 @@ void addPlayerShouldAllowReconnectWithCorrectUuid() { } @Test - void addPlayerShouldBlockReconnectWithWrongUuid() { + void addPlayerShouldBlockReconnectWithWrongUuid() { Lobby lobby = new Lobby("ABCDE", "Host"); lobby.addPlayer("P1", false); @@ -166,21 +167,14 @@ void addPlayerShouldBlockReconnectWithWrongUuid() { } @Test - void testSetPlayerUuid_playerFound() { - Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.setPlayerUuid("Host", "test-uuid-1"); + void addRestoredPlayerShouldAddPlayerDirectly() { + Lobby lobby = new Lobby("ABCDE"); + Player restoredPlayer = new Player("RestoredUser", false, "custom-uuid-999"); - Player player1 = lobby.getPlayerList().get(0); - assertEquals("test-uuid-1", player1.uuid()); - } + lobby.addRestoredPlayer(restoredPlayer); - @Test - void testSetPlayerUuid_playerNotFound() { - Lobby lobby = new Lobby("ABCDE", "Host"); - lobby.setPlayerUuid("NotHost", "test-uuid-1"); - // if i don't hit the same username, it should not be overridden - - Player player1 = lobby.getPlayerList().get(0); - assertNotEquals("test-uuid-1", player1.uuid()); + assertEquals(1, lobby.getPlayerList().size()); + assertEquals("custom-uuid-999", lobby.getPlayerList().get(0).uuid()); + assertEquals("RestoredUser", lobby.getPlayerList().get(0).username()); } -} +} \ No newline at end of file From a9b58efce4b2eff1a9123c6a407d9591048f8b31 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 23:48:38 +0200 Subject: [PATCH 165/172] fix: change required from UUID to username for lobby creation --- .../codenames/backend/lobby/api/LobbyController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index 3c18cddc..a30931f4 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -40,12 +40,12 @@ public LobbyController(LobbyService service) { /** * Handles a request to create a new lobby. * - * @param uuid the UUID of the requesting user + * @param username the UUID of the requesting user * @return a response containing the result and the generated lobby code */ @GetMapping("/create") - public ResponseEntity createLobby(@RequestParam String uuid) { - String lobbyCode = service.createLobby(uuid); + public ResponseEntity createLobby(@RequestParam String username) { + String lobbyCode = service.createLobby(username); if (lobbyCode == null || lobbyCode.isBlank()) { return ResponseEntity.internalServerError() .body(new LobbyResponse("Error while creating lobby.", "", null, false, null)); @@ -53,7 +53,7 @@ public ResponseEntity createLobby(@RequestParam String uuid) { List players = service.getPlayersDto(lobbyCode); Player host = Objects.requireNonNull( - service.getPlayer(lobbyCode, uuid)); + service.getPlayer(lobbyCode, username)); String hostUuid = host.uuid(); From 2f38bf6ac8d2a06c09090610f1c22d19e28d00c1 Mon Sep 17 00:00:00 2001 From: 5eli Date: Tue, 16 Jun 2026 23:51:22 +0200 Subject: [PATCH 166/172] style: update javaDoc --- .../codenames/codenames/backend/lobby/api/LobbyController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index a30931f4..ee744ef3 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -40,7 +40,7 @@ public LobbyController(LobbyService service) { /** * Handles a request to create a new lobby. * - * @param username the UUID of the requesting user + * @param username the username of the requesting user * @return a response containing the result and the generated lobby code */ @GetMapping("/create") From be8ae7068f6c458a88072fb6b6480f367e16cf5a Mon Sep 17 00:00:00 2001 From: Emre Orhan <116675659+Zheaver@users.noreply.github.com> Date: Wed, 17 Jun 2026 02:25:57 +0200 Subject: [PATCH 167/172] Fix cheat system message handling --- .../codenames/backend/game/api/GameSocketController.java | 9 ++++++--- .../codenames/backend/game/domain/CheatResult.java | 6 +++++- .../codenames/backend/game/domain/GameManager.java | 4 ++-- .../backend/game/api/GameSocketControllerTest.java | 7 +++---- .../backend/game/application/CheatServiceTest.java | 2 +- .../backend/game/application/GameServiceTest.java | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java index aa3b0898..9bb465d5 100644 --- a/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java +++ b/src/main/java/com/codenames/codenames/backend/game/api/GameSocketController.java @@ -77,9 +77,12 @@ public void useCheat(CheatCardMessage message) { result.message(), ChatMessageType.SYSTEM); - messagingTemplate.convertAndSendToUser( - message.getUsername(), - "/queue/system", + messagingTemplate.convertAndSend( + "/topic/chat/" + + message.getLobbyCode() + + "/" + + result.team() + + "/operative", systemMessage); persistenceService.saveSnapShot(message.getLobbyCode()); diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java b/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java index 6d5b57d1..94fdfc03 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/CheatResult.java @@ -1,8 +1,12 @@ package com.codenames.codenames.backend.game.domain; +import com.codenames.codenames.backend.lobby.domain.Team; /** * Result of a cheat attempt. * * @param message private message for the player + * @param team the team that used the cheat */ -public record CheatResult(String message) {} \ No newline at end of file +public record CheatResult( + String message, + Team team) {} \ No newline at end of file diff --git a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java index 72f0bca2..72d31828 100644 --- a/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java +++ b/src/main/java/com/codenames/codenames/backend/game/domain/GameManager.java @@ -376,11 +376,11 @@ public CheatResult useCheat(List positions, Team team) { } if (correctCards.isEmpty()) { - return new CheatResult("Keine der ausgewählten Karten ist richtig."); + return new CheatResult("Keine der ausgewählten Karten ist richtig.", team); } Card correctCard = correctCards.get(0); - return new CheatResult("Die Karte \"" + correctCard.getWord() + "\" ist richtig."); + return new CheatResult("Die Karte \"" + correctCard.getWord() + "\" ist richtig.", team); } } diff --git a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java index 358ac018..f74d08bf 100644 --- a/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/api/GameSocketControllerTest.java @@ -129,7 +129,7 @@ void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { message.setUsername("Max"); message.setPositions(List.of(0, 1)); - CheatResult result = new CheatResult("Die Karte \"Dog\" ist richtig."); + CheatResult result = new CheatResult("Die Karte \"Dog\" ist richtig.", Team.RED); when(cheatService.useCheat(LOBBY_CODE, "Max", List.of(0, 1))).thenReturn(result); @@ -138,9 +138,8 @@ void useCheatShouldSendPrivateSystemMessageAndPersistSnapshot() { verify(cheatService).useCheat(LOBBY_CODE, "Max", List.of(0, 1)); verify(messagingTemplate) - .convertAndSendToUser( - "Max", - "/queue/system", + .convertAndSend( + "/topic/chat/" + LOBBY_CODE + "/RED/operative", new ChatDto("System", result.message(), ChatMessageType.SYSTEM)); verify(persistenceService).saveSnapShot(LOBBY_CODE); diff --git a/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java index 536e0e56..dfb3d865 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/CheatServiceTest.java @@ -35,7 +35,7 @@ void setUp() { @Test void useCheatShouldReturnResultForOperative() { List positions = List.of(0, 1); - CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig."); + CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig.", Team.RED); when(lobbyService.getPlayerTeam(USERNAME, LOBBY_CODE)).thenReturn(Team.RED); when(lobbyService.getPlayerRole(USERNAME, LOBBY_CODE)).thenReturn(Role.OPERATIVE); diff --git a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java index a5e8fd75..1cfc2611 100644 --- a/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java +++ b/src/test/java/com/codenames/codenames/backend/game/application/GameServiceTest.java @@ -163,7 +163,7 @@ void getGameSnapshotsShouldReturnAllCurrentGameStates() { @Test void testUseCheat() { List positions = List.of(0, 1); - CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig."); + CheatResult expected = new CheatResult("Die Karte \"Dog\" ist richtig.", Team.RED); when(mockGameManager.useCheat(positions, redTeam)).thenReturn(expected); From c5fa25250ffcf0b2c00ce4a91cf7a48d3eeac1d3 Mon Sep 17 00:00:00 2001 From: Anna Pschernig Date: Wed, 17 Jun 2026 16:36:25 +0200 Subject: [PATCH 168/172] refactor: removed unused variable --- .../codenames/codenames/backend/lobby/api/LobbyController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java index ee744ef3..8d9f3558 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/api/LobbyController.java @@ -103,7 +103,6 @@ public ResponseEntity joinLobby( @GetMapping("/{lobbyCode}/leave") public ResponseEntity leaveLobby( @PathVariable String lobbyCode, @RequestParam String uuid) { - boolean wasStarted = service.getIsStarted(lobbyCode); boolean left = service.leaveLobby(uuid, lobbyCode); if (left) { From 59f429d491f779e37ebd10c69294fb79cfd5f03f Mon Sep 17 00:00:00 2001 From: Anna Pschernig Date: Wed, 17 Jun 2026 16:37:00 +0200 Subject: [PATCH 169/172] fix: change getPlayer from uuid to username --- .../codenames/backend/lobby/application/LobbyService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java index 4c67baad..bcbeae9e 100644 --- a/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java +++ b/src/main/java/com/codenames/codenames/backend/lobby/application/LobbyService.java @@ -292,14 +292,14 @@ public Role getPlayerRole(String uuid, String lobbyCode) { * Retrieves a specific player from a lobby. * * @param lobbyCode the lobby code - * @param uuid the UUID of the player + * @param username the username of the player * @return the Player object, or null if not found */ - public Player getPlayer(String lobbyCode, String uuid) { + public Player getPlayer(String lobbyCode, String username) { Lobby lobby = lobbyList.get(lobbyCode); if (lobby != null) { return lobby.getPlayerList().stream() - .filter(p -> p.uuid().equals(uuid)) + .filter(p -> p.username().equals(username)) .findFirst() .orElse(null); } From f78c389cb0a03b447850d91ad9635605d8739fab Mon Sep 17 00:00:00 2001 From: Anna Pschernig Date: Wed, 17 Jun 2026 16:37:13 +0200 Subject: [PATCH 170/172] fix: combine V2 flyway migration scripts --- .../V2__add_cheat_flags_to_game_state.sql | 5 ----- .../db/migration/V2__add_player_uuid.sql | 12 ----------- ...uuid_and_add_cheat_flags_to_game_state.sql | 20 +++++++++++++++++++ 3 files changed, 20 insertions(+), 17 deletions(-) delete mode 100644 src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql delete mode 100644 src/main/resources/db/migration/V2__add_player_uuid.sql create mode 100644 src/main/resources/db/migration/V2__add_player_uuid_and_add_cheat_flags_to_game_state.sql diff --git a/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql b/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql deleted file mode 100644 index 664a2dc9..00000000 --- a/src/main/resources/db/migration/V2__add_cheat_flags_to_game_state.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE game_state - ADD COLUMN red_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; - -ALTER TABLE game_state - ADD COLUMN blue_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V2__add_player_uuid.sql b/src/main/resources/db/migration/V2__add_player_uuid.sql deleted file mode 100644 index 1a73e104..00000000 --- a/src/main/resources/db/migration/V2__add_player_uuid.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE player - ADD COLUMN uuid VARCHAR(36); - -UPDATE player -SET uuid = gen_random_uuid()::text; - -ALTER TABLE player - ALTER COLUMN uuid SET NOT NULL; - -ALTER TABLE player - ADD CONSTRAINT unique_player_uuid - UNIQUE (uuid); \ No newline at end of file diff --git a/src/main/resources/db/migration/V2__add_player_uuid_and_add_cheat_flags_to_game_state.sql b/src/main/resources/db/migration/V2__add_player_uuid_and_add_cheat_flags_to_game_state.sql new file mode 100644 index 00000000..86c4a9ea --- /dev/null +++ b/src/main/resources/db/migration/V2__add_player_uuid_and_add_cheat_flags_to_game_state.sql @@ -0,0 +1,20 @@ +-- add uuid to player +ALTER TABLE player + ADD COLUMN uuid VARCHAR(36); + +UPDATE player +SET uuid = gen_random_uuid()::text; + +ALTER TABLE player + ALTER COLUMN uuid SET NOT NULL; + +ALTER TABLE player + ADD CONSTRAINT unique_player_uuid + UNIQUE (uuid); + +-- add cheat flags to game state +ALTER TABLE game_state + ADD COLUMN red_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; + +ALTER TABLE game_state + ADD COLUMN blue_team_cheat_used BOOLEAN DEFAULT FALSE NOT NULL; \ No newline at end of file From 15fd8d0f0983c450ff499c6493b45e62ddf7f53b Mon Sep 17 00:00:00 2001 From: tasaje1 Date: Wed, 17 Jun 2026 18:57:37 +0200 Subject: [PATCH 171/172] docs: add docker compose resource limits --- docker-compose.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4e8780f8..22217445 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,14 @@ services: postgres: image: postgres:16 restart: unless-stopped + deploy: + resources: + limits: + cpus: "0.25" + memory: 256M + reservations: + cpus: "0.10" + memory: 128M environment: POSTGRES_DB: codenames POSTGRES_USER: codenames_user @@ -17,9 +25,18 @@ services: backend: image: ghcr.io/ss26-se2-codenames/backend:latest restart: unless-stopped + deploy: + resources: + limits: + cpus: "0.50" + memory: 512M + reservations: + cpus: "0.25" + memory: 256M ports: - "53213:8080" environment: # Inject datasource url into application.yml when containerized + JAVA_TOOL_OPTIONS: "-XX:MaxRAMPercentage=75" SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/codenames SPRING_DATASOURCE_USERNAME: codenames_user SPRING_DATASOURCE_PASSWORD: codenames_pass @@ -28,4 +45,4 @@ services: condition: service_healthy volumes: - postgres_data: \ No newline at end of file + postgres_data: From a76414d9a5c2e56126d7c05f7f1a5279807a3c51 Mon Sep 17 00:00:00 2001 From: XtophB Date: Wed, 17 Jun 2026 19:24:31 +0200 Subject: [PATCH 172/172] chore: change jdk to jre to reduce RAM usage We move to JRE as we do not need certain build utilities --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 48d66258..7f684898 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ WORKDIR /app COPY . . RUN mvn clean package -DskipTests -FROM eclipse-temurin:17-jdk-jammy +FROM eclipse-temurin:17-jre-jammy WORKDIR /app COPY --from=build /app/target/*.jar app.jar