diff --git a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/TemplateAdminController.java b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/TemplateAdminController.java index 03f1cfe57..ae095d25e 100644 --- a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/TemplateAdminController.java +++ b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/TemplateAdminController.java @@ -28,7 +28,7 @@ public class TemplateAdminController { * return 201 status code(성공적인 추가 status) */ @PostMapping - public ResponseEntity addTemplate(@RequestBody TemplateAdminCreateReqDto request) { + public ResponseEntity addTemplate(@RequestBody @Valid TemplateAdminCreateReqDto request) { templateAdminService.addTemplate(request); return ResponseEntity.status(HttpStatus.CREATED).build(); } diff --git a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminCreateReqDto.java b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminCreateReqDto.java index 87221a120..3eb5757d6 100644 --- a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminCreateReqDto.java +++ b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminCreateReqDto.java @@ -1,5 +1,10 @@ package gg.party.api.admin.templates.controller.request; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + import gg.data.party.Category; import gg.data.party.GameTemplate; import lombok.Getter; @@ -8,16 +13,53 @@ @Getter @NoArgsConstructor public class TemplateAdminCreateReqDto { + @NotNull(message = "카테고리 ID는 필수입니다.") private Long categoryId; + + @NotBlank(message = "게임 이름은 필수이며, 비어 있을 수 없습니다.") + @Size(max = 15, message = "게임 이름은 15자를 초과할 수 없습니다.") private String gameName; + + @NotNull(message = "최대 게임 인원은 필수입니다.") + @Min(value = 2, message = "최대 게임 인원은 최소 2명 이상이어야 합니다.") private Integer maxGamePeople; + + @NotNull(message = "최소 게임 인원은 필수입니다.") + @Min(value = 2, message = "최소 게임 인원은 최소 2명 이상이어야 합니다.") private Integer minGamePeople; + + @NotNull(message = "최대 게임 시간은 필수입니다.") + @Min(value = 1, message = "최대 게임 시간은 최소 1분 이상이어야 합니다.") private Integer maxGameTime; + + @NotNull(message = "최소 게임 시간은 필수입니다.") + @Min(value = 1, message = "최소 게임 시간은 최소 1분 이상이어야 합니다.") private Integer minGameTime; + + @NotBlank(message = "장르는 필수이며, 비어 있을 수 없습니다.") + @Size(max = 10, message = "장르는 10자를 초과할 수 없습니다.") private String genre; + + @Size(max = 10, message = "난이도는 10자를 초과할 수 없습니다.") private String difficulty; + + @NotBlank(message = "내용은 필수이며, 비어 있을 수 없습니다.") + @Size(max = 100, message = "내용은 100자를 초과할 수 없습니다.") private String summary; + public TemplateAdminCreateReqDto(long categoryId, String gameName, int maxGamePeople, int minGamePeople, + int maxGameTime, int minGameTime, String genre, String difficulty, String summary) { + this.categoryId = categoryId; + this.gameName = gameName; + this.maxGamePeople = maxGamePeople; + this.minGamePeople = minGamePeople; + this.maxGameTime = maxGameTime; + this.minGameTime = minGameTime; + this.genre = genre; + this.difficulty = difficulty; + this.summary = summary; + } + public static GameTemplate toEntity(TemplateAdminCreateReqDto dto, Category category) { return GameTemplate.builder() .category(category) diff --git a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminUpdateReqDto.java b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminUpdateReqDto.java index 4a06063b1..e5eed719a 100644 --- a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminUpdateReqDto.java +++ b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/controller/request/TemplateAdminUpdateReqDto.java @@ -1,6 +1,9 @@ package gg.party.api.admin.templates.controller.request; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import lombok.Getter; import lombok.NoArgsConstructor; @@ -8,27 +11,50 @@ @Getter @NoArgsConstructor public class TemplateAdminUpdateReqDto { - @NotNull(message = "Category must not be null") + @NotNull(message = "카테고리 ID는 필수입니다.") private Long categoryId; - @NotNull(message = "Game name must not be null") + @NotBlank(message = "게임 이름은 필수이며, 비어 있을 수 없습니다.") + @Size(max = 15, message = "게임 이름은 15자를 초과할 수 없습니다.") private String gameName; - @NotNull(message = "Maximum game people must not be null") + @NotNull(message = "최대 게임 인원은 필수입니다.") + @Min(value = 2, message = "최대 게임 인원은 최소 2명 이상이어야 합니다.") private Integer maxGamePeople; - @NotNull(message = "Minimum game people must not be null") + @NotNull(message = "최소 게임 인원은 필수입니다.") + @Min(value = 2, message = "최소 게임 인원은 최소 2명 이상이어야 합니다.") private Integer minGamePeople; - @NotNull(message = "Maximum game time must not be null") + @NotNull(message = "최대 게임 시간은 필수입니다.") + @Min(value = 1, message = "최대 게임 시간은 최소 1분 이상이어야 합니다.") private Integer maxGameTime; - @NotNull(message = "Minimum game time must not be null") + @NotNull(message = "최소 게임 시간은 필수입니다.") + @Min(value = 1, message = "최소 게임 시간은 최소 1분 이상이어야 합니다.") private Integer minGameTime; + @NotBlank(message = "장르는 필수이며, 비어 있을 수 없습니다.") + @Size(max = 10, message = "장르는 10자를 초과할 수 없습니다.") private String genre; + + @Size(max = 10, message = "난이도는 10자를 초과할 수 없습니다.") private String difficulty; - @NotNull(message = "Summary must not be null") + @NotBlank(message = "내용은 필수이며, 비어 있을 수 없습니다.") + @Size(max = 100, message = "내용은 100자를 초과할 수 없습니다.") private String summary; + + public TemplateAdminUpdateReqDto(long categoryId, String gameName, int maxGamePeople, int minGamePeople, + int maxGameTime, int minGameTime, String genre, String difficulty, String summary) { + this.categoryId = categoryId; + this.gameName = gameName; + this.maxGamePeople = maxGamePeople; + this.minGamePeople = minGamePeople; + this.maxGameTime = maxGameTime; + this.minGameTime = minGameTime; + this.genre = genre; + this.difficulty = difficulty; + this.summary = summary; + } } diff --git a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/service/TemplateAdminService.java b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/service/TemplateAdminService.java index 4b68a3314..b34877770 100644 --- a/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/service/TemplateAdminService.java +++ b/gg-pingpong-api/src/main/java/gg/party/api/admin/templates/service/TemplateAdminService.java @@ -21,7 +21,7 @@ public class TemplateAdminService { /** * 템플릿 추가 - * @exception CategoryNotFoundException 존재하지 않는 카테고리 입력 + * @exception CategoryNotFoundException 존재하지 않는 카테고리 입력 - 404 */ public void addTemplate(TemplateAdminCreateReqDto request) { Category category = categoryRepository.findById(request.getCategoryId()) @@ -34,8 +34,8 @@ public void addTemplate(TemplateAdminCreateReqDto request) { /** * 템플릿 수정 - * @exception TemplateNotFoundException 존재하지 않는 템플릿 입력 - * @exception CategoryNotFoundException 존재하지 않는 카테고리 입력 + * @exception TemplateNotFoundException 존재하지 않는 템플릿 입력 - 404 + * @exception CategoryNotFoundException 존재하지 않는 카테고리 입력 - 404 */ @Transactional public void modifyTemplate(Long templateId, TemplateAdminUpdateReqDto request) { @@ -64,7 +64,7 @@ public void modifyTemplate(Long templateId, TemplateAdminUpdateReqDto request) { /** * 템플릿 삭제 - * @exception TemplateNotFoundException 존재하지 않는 템플릿 입력 + * @exception TemplateNotFoundException 존재하지 않는 템플릿 입력 - 404 */ @Transactional public void removeTemplate(Long templateId) { diff --git a/gg-pingpong-api/src/test/java/gg/party/api/admin/template/TemplateAdminControllerTest.java b/gg-pingpong-api/src/test/java/gg/party/api/admin/template/TemplateAdminControllerTest.java new file mode 100644 index 000000000..fa126d58d --- /dev/null +++ b/gg-pingpong-api/src/test/java/gg/party/api/admin/template/TemplateAdminControllerTest.java @@ -0,0 +1,243 @@ +package gg.party.api.admin.template; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.apache.http.HttpHeaders; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +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.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import gg.auth.utils.AuthTokenProvider; +import gg.data.party.Category; +import gg.data.party.GameTemplate; +import gg.data.user.User; +import gg.data.user.type.RacketType; +import gg.data.user.type.RoleType; +import gg.data.user.type.SnsType; +import gg.party.api.admin.templates.controller.request.TemplateAdminCreateReqDto; +import gg.party.api.admin.templates.controller.request.TemplateAdminUpdateReqDto; +import gg.party.api.admin.templates.service.TemplateAdminService; +import gg.repo.party.CategoryRepository; +import gg.repo.party.TemplateRepository; +import gg.utils.TestDataUtils; +import gg.utils.annotation.IntegrationTest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@IntegrationTest +@AutoConfigureMockMvc +@Transactional +@RequiredArgsConstructor +@Slf4j +public class TemplateAdminControllerTest { + @Autowired + MockMvc mockMvc; + @Autowired + TestDataUtils testDataUtils; + @Autowired + ObjectMapper objectMapper; + @Autowired + AuthTokenProvider tokenProvider; + @Autowired + CategoryRepository categoryRepository; + @Autowired + TemplateAdminService templateAdminService; + @Autowired + TemplateRepository templateRepository; + User userTester; + String userAccessToken; + Category testCategory; + GameTemplate testTemplate; + + @Nested + @DisplayName("템플릿 추가 테스트") + class AddTemplate { + @BeforeEach + void beforeEach() { + userTester = testDataUtils.createNewUser("adminTester", "adminTester", + RacketType.DUAL, SnsType.SLACK, RoleType.ADMIN); + userAccessToken = tokenProvider.createToken(userTester.getId()); + testCategory = testDataUtils.createNewCategory("category"); + } + + /** + * 템플릿을 추가 + * 어드민만 할 수 있음. + */ + @Test + @DisplayName("추가 성공 201") + public void success() throws Exception { + String url = "/party/admin/templates"; + //given + TemplateAdminCreateReqDto templateAdminCreateReqDto = new TemplateAdminCreateReqDto( + testCategory.getId(), "gameName", 4, 2, + 180, 180, "genre", "hard", "summary"); + String jsonRequest = objectMapper.writeValueAsString(templateAdminCreateReqDto); + //when + String contentAsString = mockMvc.perform(post(url) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isCreated()) + .andReturn().getResponse().getContentAsString(); + //then + assertThat(templateRepository.findAll()).isNotNull(); + } + + @Test + @DisplayName("카테고리 없음으로 인한 추가 실패 404") + public void fail() throws Exception { + String url = "/party/admin/templates"; + //given + TemplateAdminCreateReqDto templateAdminCreateReqDto = new TemplateAdminCreateReqDto( + 10L, "gameName", 4, 2, + 180, 180, "genre", "hard", "summary"); + String jsonRequest = objectMapper.writeValueAsString(templateAdminCreateReqDto); + //when && then + String contentAsString = mockMvc.perform(post(url) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNotFound()).toString(); + } + } + + @Nested + @DisplayName("템플릿 수정 테스트") + class UpdateTemplate { + @BeforeEach + void beforeEach() { + userTester = testDataUtils.createNewUser("adminTester", "adminTester", + RacketType.DUAL, SnsType.SLACK, RoleType.ADMIN); + userAccessToken = tokenProvider.createToken(userTester.getId()); + testCategory = testDataUtils.createNewCategory("category"); + testTemplate = testDataUtils.createTemplate(testCategory, "gameName", 4, + 2, 180, 180, "genre", "hard", "summary"); + } + + /** + * 템플릿 수정 + * 어드민만 할 수 있음. + */ + @Test + @DisplayName("수정 성공 204") + public void success() throws Exception { + String templateId = testTemplate.getId().toString(); + String url = "/party/admin/templates/" + templateId; + //given + Category newTestCategory = testDataUtils.createNewCategory("newCate"); + TemplateAdminUpdateReqDto templateAdminUpdateReqDto = new TemplateAdminUpdateReqDto( + newTestCategory.getId(), "newGameName", 8, 4, + 90, 90, "newGenre", "easy", "newSummary"); + String jsonRequest = objectMapper.writeValueAsString(templateAdminUpdateReqDto); + //when + String contentAsString = mockMvc.perform(patch(url) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNoContent()) + .andReturn().getResponse().getContentAsString(); + //then + assertThat(testTemplate.getGameName()).isEqualTo("newGameName"); + assertThat(testTemplate.getCategory()).isEqualTo(newTestCategory); + assertThat(testTemplate.getMaxGamePeople()).isEqualTo(8); + assertThat(testTemplate.getMinGamePeople()).isEqualTo(4); + assertThat(testTemplate.getMaxGameTime()).isEqualTo(90); + assertThat(testTemplate.getMinGameTime()).isEqualTo(90); + assertThat(testTemplate.getGenre()).isEqualTo("newGenre"); + assertThat(testTemplate.getDifficulty()).isEqualTo("easy"); + assertThat(testTemplate.getSummary()).isEqualTo("newSummary"); + } + + @Test + @DisplayName("카테고리 없음으로 인한 수정 실패 404") + public void noCategoryFail() throws Exception { + String templateId = testTemplate.getId().toString(); + String url = "/party/admin/templates/" + templateId; + //given + TemplateAdminUpdateReqDto templateAdminUpdateReqDto = new TemplateAdminUpdateReqDto( + 10L, "newGameName", 8, 4, + 90, 90, "newGenre", "easy", "newSummary"); + String jsonRequest = objectMapper.writeValueAsString(templateAdminUpdateReqDto); + //when && then + String contentAsString = mockMvc.perform(patch(url) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNotFound()).toString(); + } + + @Test + @DisplayName("템플릿 없음으로 인한 수정 실패 404") + public void noTemplateFail() throws Exception { + String templateId = "10"; + String url = "/party/admin/templates/" + templateId; + //given + Category newTestCategory = testDataUtils.createNewCategory("newCate"); + TemplateAdminUpdateReqDto templateAdminUpdateReqDto = new TemplateAdminUpdateReqDto( + newTestCategory.getId(), "newGameName", 8, 4, + 90, 90, "newGenre", "easy", "newSummary"); + String jsonRequest = objectMapper.writeValueAsString(templateAdminUpdateReqDto); + //when && then + String contentAsString = mockMvc.perform(patch(url) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNotFound()).toString(); + } + } + + @Nested + @DisplayName("템플릿 삭제 테스트") + class RemoveTemplate { + @BeforeEach + void beforeEach() { + userTester = testDataUtils.createNewUser("adminTester", "adminTester", + RacketType.DUAL, SnsType.SLACK, RoleType.ADMIN); + userAccessToken = tokenProvider.createToken(userTester.getId()); + testCategory = testDataUtils.createNewCategory("category"); + testTemplate = testDataUtils.createTemplate(testCategory, "gameName", 4, + 2, 180, 180, "genre", "hard", "summary"); + } + + /** + * 템플릿 삭제 + * 어드민만 할 수 있음. + */ + @Test + @DisplayName("삭제 성공 204") + public void success() throws Exception { + String templateId = testTemplate.getId().toString(); + String url = "/party/admin/templates/" + templateId; + //given && when + mockMvc.perform(delete(url) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNoContent()); + //then + assertThat(templateRepository.findAll()).isEmpty(); + } + + @Test + @DisplayName("템플릿 없음으로 인한 삭제 실패 404") + public void fail() throws Exception { + String templateId = "10"; + String url = "/party/admin/templates/" + templateId; + //given && when + mockMvc.perform(delete(url) + .contentType(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + userAccessToken)) + .andExpect(status().isNotFound()); + } + } +} diff --git a/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java b/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java index 6a12cd174..d0fc9e02c 100644 --- a/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java +++ b/gg-utils/src/testFixtures/java/gg/utils/TestDataUtils.java @@ -14,6 +14,7 @@ import gg.data.noti.Noti; import gg.data.noti.type.NotiType; import gg.data.party.Category; +import gg.data.party.GameTemplate; import gg.data.party.PartyPenalty; import gg.data.party.Room; import gg.data.party.UserRoom; @@ -55,6 +56,7 @@ import gg.repo.party.CategoryRepository; import gg.repo.party.PartyPenaltyRepository; import gg.repo.party.RoomRepository; +import gg.repo.party.TemplateRepository; import gg.repo.party.UserRoomRepository; import gg.repo.rank.RankRepository; import gg.repo.rank.TierRepository; @@ -95,6 +97,7 @@ public class TestDataUtils { private final UserRoomRepository userRoomRepository; private final CategoryRepository categoryRepository; private final PartyPenaltyRepository partyPenaltyRepository; + private final TemplateRepository templateRepository; public String getLoginAccessToken() { User user = User.builder() @@ -828,4 +831,20 @@ public User createNewImageUser(String intraId, String email, RacketType racketTy userRepository.save(user); return user; } + + public GameTemplate createTemplate(Category category, String gameName, Integer maxGamePeople, Integer minGamePeople, + Integer maxGameTime, Integer minGameTime, String genre, String difficulty, String summary) { + GameTemplate gameTemplate = GameTemplate.builder() + .category(category) + .gameName(gameName) + .maxGamePeople(maxGamePeople) + .minGamePeople(minGamePeople) + .maxGameTime(maxGameTime) + .minGameTime(minGameTime) + .genre(genre) + .difficulty(difficulty) + .summary(summary) + .build(); + return templateRepository.save(gameTemplate); + } }