From 64e41b663e76e6e15d09785c32182fb1d76db0c0 Mon Sep 17 00:00:00 2001 From: Phan Huy Tran Date: Wed, 26 Mar 2025 13:50:56 +0100 Subject: [PATCH] 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