From 24ea51318fca09fc4eeabad5d409d2ea625cb074 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 11:13:51 +0100 Subject: [PATCH 1/8] feat: Add card class --- .../java/de/szut/casino/blackjack/Card.java | 14 +++++++++ .../java/de/szut/casino/blackjack/Rank.java | 31 +++++++++++++++++++ .../java/de/szut/casino/blackjack/Suit.java | 20 ++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 backend/src/main/java/de/szut/casino/blackjack/Card.java create mode 100644 backend/src/main/java/de/szut/casino/blackjack/Rank.java create mode 100644 backend/src/main/java/de/szut/casino/blackjack/Suit.java diff --git a/backend/src/main/java/de/szut/casino/blackjack/Card.java b/backend/src/main/java/de/szut/casino/blackjack/Card.java new file mode 100644 index 0000000..2e8922d --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/Card.java @@ -0,0 +1,14 @@ +package de.szut.casino.blackjack; + +import lombok.Getter; + +@Getter +public class Card { + private final Suit suit; + private final Rank rank; + + public Card(Suit suit, Rank rank) { + this.suit = suit; + this.rank = rank; + } +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/Rank.java b/backend/src/main/java/de/szut/casino/blackjack/Rank.java new file mode 100644 index 0000000..8f9a3b8 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/Rank.java @@ -0,0 +1,31 @@ +package de.szut.casino.blackjack; + +import lombok.Getter; + +@Getter +public enum Rank { + TWO("2", "Two", 2), + THREE("3", "Three", 3), + FOUR("4", "Four", 4), + FIVE("5", "Five", 5), + SIX("6", "Six", 6), + SEVEN("7", "Seven", 7), + EIGHT("8", "Eight", 8), + NINE("9", "Nine", 9), + TEN("10", "Ten", 10), + JACK("J", "Jack", 10), + QUEEN("Q", "Queen", 10), + KING("K", "King", 10), + ACE("A", "Ace", 11); + + private final String symbol; + private final String displayName; + private final int value; + + Rank(String symbol, String displayName, int value) { + this.symbol = symbol; + this.displayName = displayName; + this.value = value; + } + +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/Suit.java b/backend/src/main/java/de/szut/casino/blackjack/Suit.java new file mode 100644 index 0000000..8ee80a9 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/Suit.java @@ -0,0 +1,20 @@ +package de.szut.casino.blackjack; + +import lombok.Getter; + +@Getter +public enum Suit { + HEARTS("H", "Hearts"), + DIAMONDS("D", "Diamonds"), + CLUBS("C", "Clubs"), + SPADES("S", "Spades"); + + private final String symbol; + private final String displayName; + + Suit(String symbol, String displayName) { + this.symbol = symbol; + this.displayName = displayName; + } + +} From 8cb045fcbe7b3be037a3cf8a3059ae0b4ed13e14 Mon Sep 17 00:00:00 2001 From: Jan K9f Date: Wed, 26 Mar 2025 12:20:27 +0100 Subject: [PATCH 2/8] feat(blackjack): add BlackJack game creation functionality --- backend/requests/test.http | 7 ++++ .../blackjack/BlackJackGameController.java | 35 +++++++++++++++++++ .../casino/blackjack/BlackJackGameEntity.java | 23 ++++++++++++ .../java/de/szut/casino/blackjack/Card.java | 1 + .../blackjack/dto/CreateBlackJackGameDto.java | 14 ++++++++ 5 files changed, 80 insertions(+) create mode 100644 backend/requests/test.http create mode 100644 backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java create mode 100644 backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java create mode 100644 backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java diff --git a/backend/requests/test.http b/backend/requests/test.http new file mode 100644 index 0000000..f7ebab0 --- /dev/null +++ b/backend/requests/test.http @@ -0,0 +1,7 @@ +POST http://localhost:8080/blackjack/start +Authorization: Bearer {{token}} +Content-Type: application/json + +{ + "betAmount": 999 +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java new file mode 100644 index 0000000..3f7fda0 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -0,0 +1,35 @@ +package de.szut.casino.blackjack; + +import de.szut.casino.blackjack.dto.CreateBlackJackGameDto; +import de.szut.casino.user.UserService; +import de.szut.casino.user.dto.CreateUserDto; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +public class BlackJackGameController { + + @Autowired + private UserService userService; + + @PostMapping("/blackjack/start") + public ResponseEntity createBlackJackGame(@RequestBody @Valid CreateBlackJackGameDto gameData, @RequestHeader("Authorization") String token) { + if (gameData.getBetAmount() <= 0 || gameData.getBetAmount() > userService.getCurrentUser(token).getBalance().intValue()) { + return ResponseEntity.badRequest().body("Invalid bet amount"); + } + + BlackJackGameEntity game = new BlackJackGameEntity(); + game.setBet(gameData.getBetAmount()); + System.out.println("Balls: "); + System.out.println(game.getBet()); + return null; + } +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java new file mode 100644 index 0000000..c275953 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java @@ -0,0 +1,23 @@ +package de.szut.casino.blackjack; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; + +@Entity +@Getter +@Setter +public class BlackJackGameEntity { + @Id + @GeneratedValue + private Long id; + private ArrayList playerHand; + private ArrayList dealerHand; + private ArrayList deck; + private String state; + private int bet; +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/Card.java b/backend/src/main/java/de/szut/casino/blackjack/Card.java index 2e8922d..2547739 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/Card.java +++ b/backend/src/main/java/de/szut/casino/blackjack/Card.java @@ -1,5 +1,6 @@ package de.szut.casino.blackjack; + import lombok.Getter; @Getter diff --git a/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java b/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java new file mode 100644 index 0000000..4907dd1 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java @@ -0,0 +1,14 @@ +package de.szut.casino.blackjack.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CreateBlackJackGameDto { + private int betAmount; +} From 1a4b3f073f0a1e9a8e554c32ce434a08fadeba46 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 13:05:24 +0100 Subject: [PATCH 3/8] fix: add openid scope to get bearer token request --- backend/requests/getBearerToken.http | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requests/getBearerToken.http b/backend/requests/getBearerToken.http index afdea20..791c3a2 100644 --- a/backend/requests/getBearerToken.http +++ b/backend/requests/getBearerToken.http @@ -1,6 +1,6 @@ POST http://localhost:9090/realms/LF12/protocol/openid-connect/token Content-Type: application/x-www-form-urlencoded -grant_type=password&client_id=lf12&username=lf12_test_user&password=secret +grant_type=password&client_id=lf12&username=lf12_test_user&password=secret&scope=openid > {% client.global.set("token", response.body.access_token); %} \ No newline at end of file From b2b0bb2f445a8ce939a7c37b481ef62a183b105e Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 13:23:27 +0100 Subject: [PATCH 4/8] feat: Validate bet amount --- backend/requests/test.http | 2 +- .../blackjack/BlackJackGameController.java | 29 ++++++++++++++----- .../blackjack/dto/CreateBlackJackGameDto.java | 4 ++- .../java/de/szut/casino/user/UserService.java | 1 - 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/backend/requests/test.http b/backend/requests/test.http index f7ebab0..57fd8f1 100644 --- a/backend/requests/test.http +++ b/backend/requests/test.http @@ -3,5 +3,5 @@ Authorization: Bearer {{token}} Content-Type: application/json { - "betAmount": 999 + "betAmount": -1 } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java index 3f7fda0..095a20e 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -1,8 +1,10 @@ package de.szut.casino.blackjack; import de.szut.casino.blackjack.dto.CreateBlackJackGameDto; +import de.szut.casino.user.UserEntity; import de.szut.casino.user.UserService; import de.szut.casino.user.dto.CreateUserDto; +import de.szut.casino.user.dto.GetUserDto; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -13,6 +15,10 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + @Slf4j @RestController public class BlackJackGameController { @@ -21,15 +27,24 @@ public class BlackJackGameController { private UserService userService; @PostMapping("/blackjack/start") - public ResponseEntity createBlackJackGame(@RequestBody @Valid CreateBlackJackGameDto gameData, @RequestHeader("Authorization") String token) { - if (gameData.getBetAmount() <= 0 || gameData.getBetAmount() > userService.getCurrentUser(token).getBalance().intValue()) { - return ResponseEntity.badRequest().body("Invalid bet amount"); + public ResponseEntity createBlackJackGame(@RequestBody @Valid CreateBlackJackGameDto createBlackJackGameDto, @RequestHeader("Authorization") String token) { + GetUserDto getUserDto = userService.getCurrentUser(token); + BigDecimal balance = getUserDto.getBalance(); + + if (createBlackJackGameDto.getBetAmount().compareTo(BigDecimal.ZERO) <= 0) { + Map errorResponse = new HashMap<>(); + errorResponse.put("error", "Invalid bet amount"); + return ResponseEntity.badRequest().body(errorResponse); } - BlackJackGameEntity game = new BlackJackGameEntity(); - game.setBet(gameData.getBetAmount()); - System.out.println("Balls: "); - System.out.println(game.getBet()); + if (createBlackJackGameDto.getBetAmount().compareTo(balance) > 0) { + Map errorResponse = new HashMap<>(); + errorResponse.put("error", "Insufficient funds"); + return ResponseEntity.badRequest().body(errorResponse); + } + + + return null; } } diff --git a/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java b/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java index 4907dd1..e5b0c97 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java +++ b/backend/src/main/java/de/szut/casino/blackjack/dto/CreateBlackJackGameDto.java @@ -5,10 +5,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.math.BigDecimal; + @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class CreateBlackJackGameDto { - private int betAmount; + private BigDecimal betAmount; } diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index 724962e..70d961f 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -40,7 +40,6 @@ public class UserService { public GetUserDto getCurrentUser(String token) { KeycloakUserDto userData = getKeycloakUserInfo(token); - if (userData == null) { return null; } From 64e41b663e76e6e15d09785c32182fb1d76db0c0 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 13:50:56 +0100 Subject: [PATCH 5/8] feat: Subtract user balance, persist game --- backend/requests/test.http | 2 +- .../blackjack/BlackJackGameController.java | 44 +++++-- .../casino/blackjack/BlackJackGameEntity.java | 10 +- .../blackjack/BlackJackGameRepository.java | 12 ++ .../de/szut/casino/user/UserController.java | 2 +- .../java/de/szut/casino/user/UserService.java | 11 +- .../szut/casino/user/UserControllerTest.java | 122 ------------------ 7 files changed, 61 insertions(+), 142 deletions(-) create mode 100644 backend/src/main/java/de/szut/casino/blackjack/BlackJackGameRepository.java delete mode 100644 backend/src/test/java/de/szut/casino/user/UserControllerTest.java diff --git a/backend/requests/test.http b/backend/requests/test.http index 57fd8f1..ea20985 100644 --- a/backend/requests/test.http +++ b/backend/requests/test.http @@ -3,5 +3,5 @@ Authorization: Bearer {{token}} Content-Type: application/json { - "betAmount": -1 + "betAmount": 1.01 } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java index 095a20e..e0c6378 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -2,13 +2,11 @@ package de.szut.casino.blackjack; import de.szut.casino.blackjack.dto.CreateBlackJackGameDto; import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; import de.szut.casino.user.UserService; -import de.szut.casino.user.dto.CreateUserDto; -import de.szut.casino.user.dto.GetUserDto; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -18,33 +16,53 @@ import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Slf4j @RestController public class BlackJackGameController { - @Autowired - private UserService userService; + private final UserService userService; + private final BlackJackGameRepository blackJackGameRepository; + private final UserRepository userRepository; + + public BlackJackGameController(UserService userService, BlackJackGameRepository blackJackGameRepository, UserRepository userRepository) { + this.userService = userService; + this.blackJackGameRepository = blackJackGameRepository; + this.userRepository = userRepository; + } @PostMapping("/blackjack/start") - public ResponseEntity createBlackJackGame(@RequestBody @Valid CreateBlackJackGameDto createBlackJackGameDto, @RequestHeader("Authorization") String token) { - GetUserDto getUserDto = userService.getCurrentUser(token); - BigDecimal balance = getUserDto.getBalance(); + public ResponseEntity createBlackJackGame(@RequestBody @Valid CreateBlackJackGameDto createBlackJackGameDto, @RequestHeader("Authorization") String token) { + Optional optionalUser = userService.getCurrentUser(token); - if (createBlackJackGameDto.getBetAmount().compareTo(BigDecimal.ZERO) <= 0) { + if (optionalUser.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + UserEntity user = optionalUser.get(); + BigDecimal balance = user.getBalance(); + BigDecimal betAmount = createBlackJackGameDto.getBetAmount(); + + if (betAmount.compareTo(BigDecimal.ZERO) <= 0) { Map errorResponse = new HashMap<>(); errorResponse.put("error", "Invalid bet amount"); return ResponseEntity.badRequest().body(errorResponse); } - if (createBlackJackGameDto.getBetAmount().compareTo(balance) > 0) { + if (betAmount.compareTo(balance) > 0) { Map errorResponse = new HashMap<>(); errorResponse.put("error", "Insufficient funds"); return ResponseEntity.badRequest().body(errorResponse); } + BlackJackGameEntity game = new BlackJackGameEntity(); + game.setBet(betAmount); + user.setBalance(user.getBalance().subtract(betAmount)); - return null; - } + userRepository.save(user); + blackJackGameRepository.save(game); + + return ResponseEntity.ok(game); } } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java index c275953..b9687c4 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java @@ -3,21 +3,23 @@ package de.szut.casino.blackjack; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +import java.math.BigDecimal; import java.util.ArrayList; @Entity @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class BlackJackGameEntity { @Id @GeneratedValue private Long id; - private ArrayList playerHand; - private ArrayList dealerHand; - private ArrayList deck; private String state; - private int bet; + private BigDecimal bet; } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameRepository.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameRepository.java new file mode 100644 index 0000000..d25a180 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameRepository.java @@ -0,0 +1,12 @@ +package de.szut.casino.blackjack; + +import de.szut.casino.user.UserEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public interface BlackJackGameRepository extends JpaRepository { +} diff --git a/backend/src/main/java/de/szut/casino/user/UserController.java b/backend/src/main/java/de/szut/casino/user/UserController.java index 4d232ac..3750c8b 100644 --- a/backend/src/main/java/de/szut/casino/user/UserController.java +++ b/backend/src/main/java/de/szut/casino/user/UserController.java @@ -44,7 +44,7 @@ public class UserController { @GetMapping("/user") public ResponseEntity getCurrentUser(@RequestHeader("Authorization") String token) { - GetUserDto userData = userService.getCurrentUser(token); + GetUserDto userData = userService.getCurrentUserAsDto(token); if (userData == null) { return ResponseEntity.notFound().build(); diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index 70d961f..36569e6 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -38,15 +38,24 @@ public class UserService { return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); } - public GetUserDto getCurrentUser(String token) { + public GetUserDto getCurrentUserAsDto(String token) { KeycloakUserDto userData = getKeycloakUserInfo(token); + if (userData == null) { return null; } Optional user = this.userRepository.findOneByKeycloakId(userData.getSub()); return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); + } + public Optional getCurrentUser(String token) { + KeycloakUserDto userData = getKeycloakUserInfo(token); + + if (userData == null) { + return Optional.empty(); + } + return this.userRepository.findOneByKeycloakId(userData.getSub()); } private KeycloakUserDto getKeycloakUserInfo(String token) { diff --git a/backend/src/test/java/de/szut/casino/user/UserControllerTest.java b/backend/src/test/java/de/szut/casino/user/UserControllerTest.java deleted file mode 100644 index 2addb43..0000000 --- a/backend/src/test/java/de/szut/casino/user/UserControllerTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package de.szut.casino.user; - -import static org.mockito.ArgumentMatchers.any; -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; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -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.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import de.szut.casino.user.dto.CreateUserDto; -import de.szut.casino.user.dto.GetUserDto; - -@WebMvcTest(UserController.class) -@AutoConfigureMockMvc(addFilters = false) -public class UserControllerTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @MockBean - private UserService userService; - - private GetUserDto getUserDto; - private CreateUserDto createUserDto; - private UserEntity testUser; - private final String TEST_ID = "test-id-123"; - private final String AUTH_TOKEN = "Bearer test-token"; - - @BeforeEach - void setUp() { - getUserDto = new GetUserDto(); - getUserDto.setKeycloakId(TEST_ID); - getUserDto.setUsername("testuser"); - - testUser = new UserEntity(); - testUser.setKeycloakId(TEST_ID); - testUser.setUsername("testuser"); - - createUserDto = new CreateUserDto(); - createUserDto.setKeycloakId(TEST_ID); - createUserDto.setUsername("testuser"); - } - - @Test - void getUserByIdSuccess() throws Exception { - when(userService.exists(TEST_ID)).thenReturn(true); - when(userService.getUser(TEST_ID)).thenReturn(getUserDto); - - mockMvc.perform(get("/user/" + TEST_ID)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.keycloakId").value(TEST_ID)) - .andExpect(jsonPath("$.username").value("testuser")); - } - - @Test - void getUserByIdNotFound() throws Exception { - when(userService.exists(TEST_ID)).thenReturn(false); - - mockMvc.perform(get("/user/" + TEST_ID)) - .andExpect(status().isNotFound()); - } - - @Test - void createUserSuccess() throws Exception { - when(userService.exists(TEST_ID)).thenReturn(false); - when(userService.createUser(any(CreateUserDto.class))).thenReturn(testUser); - - mockMvc.perform(post("/user") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createUserDto))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.keycloakId").value(TEST_ID)) - .andExpect(jsonPath("$.username").value("testuser")); - } - - @Test - void createUserAlreadyExists() throws Exception { - when(userService.exists(TEST_ID)).thenReturn(true); - - mockMvc.perform(post("/user") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(createUserDto))) - .andExpect(status().isFound()) - .andExpect(header().string("Location", "/user/" + TEST_ID)); - } - - @Test - void getCurrentUserSuccess() throws Exception { - when(userService.getCurrentUser(AUTH_TOKEN)).thenReturn(getUserDto); - - mockMvc.perform(get("/user") - .header("Authorization", AUTH_TOKEN)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.keycloakId").value(TEST_ID)) - .andExpect(jsonPath("$.username").value("testuser")); - } - - @Test - void getCurrentUserNotFound() throws Exception { - when(userService.getCurrentUser(anyString())).thenReturn(null); - - mockMvc.perform(get("/user") - .header("Authorization", AUTH_TOKEN)) - .andExpect(status().isNotFound()); - } -} \ No newline at end of file From 85d2b218aaa794878d1e1e3fa12ab131afeed8f6 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 13:56:27 +0100 Subject: [PATCH 6/8] feat: Add user relation to black jack game entity --- backend/requests/{test.http => blackjack.http} | 0 .../de/szut/casino/blackjack/BlackJackGameController.java | 4 +++- .../de/szut/casino/blackjack/BlackJackGameEntity.java | 8 +++++--- .../szut/casino/blackjack/{Card.java => CardEntity.java} | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) rename backend/requests/{test.http => blackjack.http} (100%) rename backend/src/main/java/de/szut/casino/blackjack/{Card.java => CardEntity.java} (68%) diff --git a/backend/requests/test.http b/backend/requests/blackjack.http similarity index 100% rename from backend/requests/test.http rename to backend/requests/blackjack.http diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java index e0c6378..cb8f6fd 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -57,6 +57,7 @@ public class BlackJackGameController { } BlackJackGameEntity game = new BlackJackGameEntity(); + game.setUser(user); game.setBet(betAmount); user.setBalance(user.getBalance().subtract(betAmount)); @@ -64,5 +65,6 @@ public class BlackJackGameController { userRepository.save(user); blackJackGameRepository.save(game); - return ResponseEntity.ok(game); } + return ResponseEntity.ok(game); + } } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java index b9687c4..9625c92 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java @@ -1,8 +1,7 @@ package de.szut.casino.blackjack; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; +import de.szut.casino.user.UserEntity; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,6 +19,9 @@ public class BlackJackGameEntity { @Id @GeneratedValue private Long id; + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private UserEntity user; private String state; private BigDecimal bet; } diff --git a/backend/src/main/java/de/szut/casino/blackjack/Card.java b/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java similarity index 68% rename from backend/src/main/java/de/szut/casino/blackjack/Card.java rename to backend/src/main/java/de/szut/casino/blackjack/CardEntity.java index 2547739..f03a5a1 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/Card.java +++ b/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java @@ -4,11 +4,11 @@ package de.szut.casino.blackjack; import lombok.Getter; @Getter -public class Card { +public class CardEntity { private final Suit suit; private final Rank rank; - public Card(Suit suit, Rank rank) { + public CardEntity(Suit suit, Rank rank) { this.suit = suit; this.rank = rank; } From 1b9bc90920f3baf1eb39ec13e8cbb76fd6583b87 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 14:20:31 +0100 Subject: [PATCH 7/8] feat: Adjust json responses --- .../blackjack/BlackJackGameController.java | 12 +++++++- .../casino/blackjack/BlackJackGameEntity.java | 19 ++++++++++++ .../casino/blackjack/BlackJackService.java | 17 +++++++++++ .../de/szut/casino/blackjack/CardEntity.java | 30 +++++++++++++++---- 4 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java index cb8f6fd..0b385f1 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -23,11 +23,13 @@ import java.util.Optional; public class BlackJackGameController { private final UserService userService; + private final BlackJackService blackJackService; private final BlackJackGameRepository blackJackGameRepository; private final UserRepository userRepository; - public BlackJackGameController(UserService userService, BlackJackGameRepository blackJackGameRepository, UserRepository userRepository) { + public BlackJackGameController(UserService userService, BlackJackService blackJackService, BlackJackGameRepository blackJackGameRepository, UserRepository userRepository) { this.userService = userService; + this.blackJackService = blackJackService; this.blackJackGameRepository = blackJackGameRepository; this.userRepository = userRepository; } @@ -60,6 +62,14 @@ public class BlackJackGameController { game.setUser(user); game.setBet(betAmount); + for (int i = 0; i < 2; i++) { + CardEntity playerCard = blackJackService.createRandomCard(game); + game.getPlayerCards().add(playerCard); + } + + CardEntity dealerCard = blackJackService.createRandomCard(game); + game.getDealerCards().add(dealerCard); + user.setBalance(user.getBalance().subtract(betAmount)); userRepository.save(user); diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java index 9625c92..e3380a7 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java @@ -1,5 +1,7 @@ package de.szut.casino.blackjack; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonManagedReference; import de.szut.casino.user.UserEntity; import jakarta.persistence.*; import lombok.AllArgsConstructor; @@ -9,6 +11,7 @@ import lombok.Setter; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.List; @Entity @Getter @@ -19,9 +22,25 @@ public class BlackJackGameEntity { @Id @GeneratedValue private Long id; + @ManyToOne @JoinColumn(name = "user_id", nullable = false) + @JsonIgnore private UserEntity user; + + // Expose UserID to JSON output + public Long getUserId() { + return user != null ? user.getId() : null; + } + private String state; private BigDecimal bet; + + @OneToMany(mappedBy = "game", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private List playerCards = new ArrayList<>(); + + @OneToMany(mappedBy = "game", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private List dealerCards = new ArrayList<>(); } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java new file mode 100644 index 0000000..8defe57 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java @@ -0,0 +1,17 @@ +package de.szut.casino.blackjack; + +import org.springframework.stereotype.Service; +import java.util.Random; + +@Service +public class BlackJackService { + private final Random random = new Random(); + + public CardEntity createRandomCard(BlackJackGameEntity game) { + CardEntity card = new CardEntity(); + card.setGame(game); + card.setSuit(Suit.values()[random.nextInt(Suit.values().length)]); + card.setRank(Rank.values()[random.nextInt(Rank.values().length)]); + return card; + } +} diff --git a/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java b/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java index f03a5a1..8026d72 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java +++ b/backend/src/main/java/de/szut/casino/blackjack/CardEntity.java @@ -1,15 +1,33 @@ package de.szut.casino.blackjack; +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +@Entity @Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor public class CardEntity { - private final Suit suit; - private final Rank rank; + @Id + @GeneratedValue + @JsonIgnore + private Long id; - public CardEntity(Suit suit, Rank rank) { - this.suit = suit; - this.rank = rank; - } + @ManyToOne + @JoinColumn(name = "game_id", nullable = false) + @JsonBackReference + private BlackJackGameEntity game; + + @Enumerated(EnumType.STRING) + private Suit suit; + + @Enumerated(EnumType.STRING) + private Rank rank; } From ca87c684b1abf83e6a714c1fd22a992ee05f6310 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 14:31:32 +0100 Subject: [PATCH 8/8] refactor: Extract game creation logic to service --- .../blackjack/BlackJackGameController.java | 29 ++------------- .../casino/blackjack/BlackJackService.java | 35 ++++++++++++++++++- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java index 0b385f1..0aab2cb 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameController.java @@ -2,11 +2,9 @@ package de.szut.casino.blackjack; import de.szut.casino.blackjack.dto.CreateBlackJackGameDto; import de.szut.casino.user.UserEntity; -import de.szut.casino.user.UserRepository; import de.szut.casino.user.UserService; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -24,14 +22,10 @@ public class BlackJackGameController { private final UserService userService; private final BlackJackService blackJackService; - private final BlackJackGameRepository blackJackGameRepository; - private final UserRepository userRepository; - public BlackJackGameController(UserService userService, BlackJackService blackJackService, BlackJackGameRepository blackJackGameRepository, UserRepository userRepository) { - this.userService = userService; + public BlackJackGameController(UserService userService, BlackJackService blackJackService) { this.blackJackService = blackJackService; - this.blackJackGameRepository = blackJackGameRepository; - this.userRepository = userRepository; + this.userService = userService; } @PostMapping("/blackjack/start") @@ -58,23 +52,6 @@ public class BlackJackGameController { return ResponseEntity.badRequest().body(errorResponse); } - BlackJackGameEntity game = new BlackJackGameEntity(); - game.setUser(user); - game.setBet(betAmount); - - for (int i = 0; i < 2; i++) { - CardEntity playerCard = blackJackService.createRandomCard(game); - game.getPlayerCards().add(playerCard); - } - - CardEntity dealerCard = blackJackService.createRandomCard(game); - game.getDealerCards().add(dealerCard); - - user.setBalance(user.getBalance().subtract(betAmount)); - - userRepository.save(user); - blackJackGameRepository.save(game); - - return ResponseEntity.ok(game); + return ResponseEntity.ok(blackJackService.createBlackJackGame(user, betAmount)); } } diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java index 8defe57..ba5e7d1 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java @@ -1,13 +1,46 @@ package de.szut.casino.blackjack; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; import org.springframework.stereotype.Service; + +import java.math.BigDecimal; import java.util.Random; @Service public class BlackJackService { + private final BlackJackGameRepository blackJackGameRepository; + private final UserRepository userRepository; + + public BlackJackService(BlackJackGameRepository blackJackGameRepository, UserRepository userRepository) { + this.blackJackGameRepository = blackJackGameRepository; + this.userRepository = userRepository; + } + private final Random random = new Random(); - public CardEntity createRandomCard(BlackJackGameEntity game) { + public BlackJackGameEntity createBlackJackGame(UserEntity user, BigDecimal betAmount) { + BlackJackGameEntity game = new BlackJackGameEntity(); + game.setUser(user); + game.setBet(betAmount); + + for (int i = 0; i < 2; i++) { + CardEntity playerCard = createRandomCard(game); + game.getPlayerCards().add(playerCard); + } + + CardEntity dealerCard = createRandomCard(game); + game.getDealerCards().add(dealerCard); + + user.setBalance(user.getBalance().subtract(betAmount)); + + userRepository.save(user); + blackJackGameRepository.save(game); + + return game; + } + + private CardEntity createRandomCard(BlackJackGameEntity game) { CardEntity card = new CardEntity(); card.setGame(game); card.setSuit(Suit.values()[random.nextInt(Suit.values().length)]);