From f33cc2f61dc35c7fbb3f2830f667500875f91bda Mon Sep 17 00:00:00 2001 From: parksey Date: Tue, 28 Nov 2023 15:50:12 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EC=9D=98=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=20redis=EC=97=90=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=82=AD=EC=A0=9C,=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/member/MemberMapper.java | 9 +++++ .../api/application/member/MemberService.java | 16 +++++++-- .../application/ranking/RankingService.java | 36 +++++++++++++++++++ .../com/moabam/api/domain/member/Member.java | 1 - .../moabam/api/dto/ranking/RankingInfo.java | 12 +++++++ .../redis/ZSetRedisRepository.java | 25 +++++++++++-- .../api/presentation/RoomController.java | 3 ++ 7 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/moabam/api/application/ranking/RankingService.java create mode 100644 src/main/java/com/moabam/api/dto/ranking/RankingInfo.java diff --git a/src/main/java/com/moabam/api/application/member/MemberMapper.java b/src/main/java/com/moabam/api/application/member/MemberMapper.java index 148a0b8d..600f16f6 100644 --- a/src/main/java/com/moabam/api/application/member/MemberMapper.java +++ b/src/main/java/com/moabam/api/application/member/MemberMapper.java @@ -19,6 +19,7 @@ import com.moabam.api.dto.member.MemberInfo; import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; +import com.moabam.api.dto.ranking.RankingInfo; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -90,4 +91,12 @@ private static Map defaultSkins(String morningImage, String nigh return birdsSkin; } + + public static RankingInfo toRankingInfo(Member member) { + return RankingInfo.builder() + .memberId(member.getId()) + .nickname(member.getNickname()) + .image(member.getProfileImage()) + .build(); + } } diff --git a/src/main/java/com/moabam/api/application/member/MemberService.java b/src/main/java/com/moabam/api/application/member/MemberService.java index b6505ce3..2fd08926 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import com.moabam.api.application.auth.mapper.AuthMapper; +import com.moabam.api.application.ranking.RankingService; import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; import com.moabam.api.domain.item.repository.InventoryRepository; @@ -23,6 +24,7 @@ import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; import com.moabam.api.dto.member.ModifyMemberRequest; +import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.common.util.BaseDataCode; import com.moabam.global.common.util.ClockHolder; @@ -38,6 +40,7 @@ @RequiredArgsConstructor public class MemberService { + private final RankingService rankingService; private final MemberRepository memberRepository; private final InventoryRepository inventoryRepository; private final ItemRepository itemRepository; @@ -72,6 +75,7 @@ public void delete(Member member) { member.delete(clockHolder.times()); memberRepository.flush(); memberRepository.delete(member); + rankingService.removeRanking(MemberMapper.toRankingInfo(member)); } public MemberInfoResponse searchInfo(AuthMember authMember, Long memberId) { @@ -82,21 +86,24 @@ public MemberInfoResponse searchInfo(AuthMember authMember, Long memberId) { searchId = memberId; } MemberInfoSearchResponse memberInfoSearchResponse = findMemberInfo(searchId, isMe); + return MemberMapper.toMemberInfoResponse(memberInfoSearchResponse); } @Transactional public void modifyInfo(AuthMember authMember, ModifyMemberRequest modifyMemberRequest, String newProfileUri) { validateNickname(modifyMemberRequest.nickname()); - Member member = memberSearchRepository.findMember(authMember.id()) .orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND)); + RankingInfo beforeInfo = MemberMapper.toRankingInfo(member); member.changeNickName(modifyMemberRequest.nickname()); member.changeIntro(modifyMemberRequest.intro()); member.changeProfileUri(newProfileUri); - memberRepository.save(member); + RankingInfo afterInfo = MemberMapper.toRankingInfo(member); + + rankingService.changeInfos(beforeInfo, afterInfo); } private void validateNickname(String nickname) { @@ -107,7 +114,10 @@ private void validateNickname(String nickname) { private Member signUp(Long socialId) { Member member = MemberMapper.toMember(socialId); - return memberRepository.save(member); + Member savedMember = memberRepository.save(member); + rankingService.addRanking(MemberMapper.toRankingInfo(member), member.getTotalCertifyCount()); + + return savedMember; } private void saveMyEgg(Member member) { diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java new file mode 100644 index 00000000..8460e210 --- /dev/null +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -0,0 +1,36 @@ +package com.moabam.api.application.ranking; + +import org.springframework.stereotype.Service; + +import com.moabam.api.application.member.MemberMapper; +import com.moabam.api.domain.member.Member; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.room.CertifiedMemberInfo; +import com.moabam.api.infrastructure.redis.ZSetRedisRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class RankingService { + + private final ZSetRedisRepository zSetRedisRepository; + + public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { + zSetRedisRepository.add("Ranking", rankingInfo, totalCertifyCount); + } + + public void updateCacheScore(CertifiedMemberInfo info) { + Member member = info.member(); + RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); + zSetRedisRepository.add("Ranking", rankingInfo, member.getTotalCertifyCount()); + } + + public void changeInfos(RankingInfo before, RankingInfo after) { + zSetRedisRepository.changeMember("Ranking", before, after); + } + + public void removeRanking(RankingInfo rankingInfo) { + zSetRedisRepository.delete("Ranking", rankingInfo); + } +} diff --git a/src/main/java/com/moabam/api/domain/member/Member.java b/src/main/java/com/moabam/api/domain/member/Member.java index 3cfc9f24..b2ede937 100644 --- a/src/main/java/com/moabam/api/domain/member/Member.java +++ b/src/main/java/com/moabam/api/domain/member/Member.java @@ -146,7 +146,6 @@ public void changeIntro(String intro) { public void changeProfileUri(String newProfileUri) { this.profileImage = requireNonNullElse(newProfileUri, profileImage); - this.profileImage = requireNonNullElse(newProfileUri, profileImage); } public void changeDefaultSkintUrl(Item item) throws NotFoundException { diff --git a/src/main/java/com/moabam/api/dto/ranking/RankingInfo.java b/src/main/java/com/moabam/api/dto/ranking/RankingInfo.java new file mode 100644 index 00000000..46645389 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/ranking/RankingInfo.java @@ -0,0 +1,12 @@ +package com.moabam.api.dto.ranking; + +import lombok.Builder; + +@Builder +public record RankingInfo( + Long memberId, + String nickname, + String image +) { + +} diff --git a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java index 9ac7c607..5dce1e42 100644 --- a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java +++ b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java @@ -18,9 +18,7 @@ public class ZSetRedisRepository { public void addIfAbsent(String key, Object value, double score) { if (redisTemplate.opsForZSet().score(key, value) == null) { - redisTemplate - .opsForZSet() - .add(requireNonNull(key), requireNonNull(value), score); + add(key, value, score); } } @@ -29,4 +27,25 @@ public Set> popMin(String key, long count) { .opsForZSet() .popMin(key, count); } + + public void add(String key, Object value, double score) { + redisTemplate + .opsForZSet() + .add(requireNonNull(key), requireNonNull(value), score); + } + + public void changeMember(String key, Object before, Object after) { + Double score = redisTemplate.opsForZSet().score(key, before); + + if (score == null) { + return; + } + + delete(key, before); + add(key, after, score); + } + + public void delete(String key, Object value) { + redisTemplate.opsForZSet().remove(key, value); + } } diff --git a/src/main/java/com/moabam/api/presentation/RoomController.java b/src/main/java/com/moabam/api/presentation/RoomController.java index ce4725e3..370d050e 100644 --- a/src/main/java/com/moabam/api/presentation/RoomController.java +++ b/src/main/java/com/moabam/api/presentation/RoomController.java @@ -18,6 +18,7 @@ import org.springframework.web.multipart.MultipartFile; import com.moabam.api.application.image.ImageService; +import com.moabam.api.application.ranking.RankingService; import com.moabam.api.application.room.CertificationService; import com.moabam.api.application.room.RoomService; import com.moabam.api.application.room.SearchService; @@ -48,6 +49,7 @@ public class RoomController { private final SearchService searchService; private final CertificationService certificationService; private final ImageService imageService; + private final RankingService rankingService; @PostMapping @ResponseStatus(HttpStatus.CREATED) @@ -114,6 +116,7 @@ public void certifyRoom(@Auth AuthMember authMember, @PathVariable("roomId") Lon List imageUrls = imageService.uploadImages(multipartFiles, ImageType.CERTIFICATION); CertifiedMemberInfo info = certificationService.getCertifiedMemberInfo(authMember.id(), roomId, imageUrls); certificationService.certifyRoom(info); + rankingService.updateCacheScore(info); } @PutMapping("/{roomId}/members/{memberId}/mandate") From 490b4a997fa27a59f4a892716fb2de6caea6e3de Mon Sep 17 00:00:00 2001 From: parksey Date: Wed, 29 Nov 2023 00:42:28 +0900 Subject: [PATCH 02/20] =?UTF-8?q?test:=20=ED=9A=8C=EC=9B=90=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=9E=AD?= =?UTF-8?q?=ED=82=B9=20=EC=B0=B8=EC=97=AC,=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/member/MemberServiceTest.java | 4 + .../ranking/RankingServiceTest.java | 163 ++++++++++++++++++ .../presentation/MemberControllerTest.java | 23 +++ .../support/fixture/RankingInfoFixture.java | 5 + 4 files changed, 195 insertions(+) create mode 100644 src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java create mode 100644 src/test/java/com/moabam/support/fixture/RankingInfoFixture.java diff --git a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java index a7959641..20dc1234 100644 --- a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java +++ b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java @@ -16,6 +16,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import com.moabam.api.application.ranking.RankingService; import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; import com.moabam.api.domain.item.repository.InventoryRepository; @@ -60,6 +61,9 @@ class MemberServiceTest { @Mock InventoryRepository inventoryRepository; + @Mock + RankingService rankingService; + @Mock ItemRepository itemRepository; diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java new file mode 100644 index 00000000..01b967dc --- /dev/null +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -0,0 +1,163 @@ +package com.moabam.api.application.ranking; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +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.context.SpringBootTest; +import org.springframework.data.redis.core.RedisTemplate; + +import com.moabam.api.application.member.MemberMapper; +import com.moabam.api.domain.member.Member; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.room.CertifiedMemberInfo; +import com.moabam.api.infrastructure.redis.ZSetRedisRepository; +import com.moabam.global.config.EmbeddedRedisConfig; +import com.moabam.support.fixture.BugFixture; + +@SpringBootTest(classes = {EmbeddedRedisConfig.class, RankingService.class, ZSetRedisRepository.class}) +public class RankingServiceTest { + + @Autowired + ZSetRedisRepository zSetRedisRepository; + + @Autowired + RedisTemplate redisTemplate; + + @Autowired + RankingService rankingService; + + @DisplayName("redis에 추가") + @Nested + class Add { + + @DisplayName("성공") + @Test + void add_success() { + // given + Long totalCertifyCount = 0L; + RankingInfo rankingInfo = RankingInfo.builder() + .image("https://image.moabam.com/test") + .memberId(1L) + .nickname("nickname") + .build(); + + // when + rankingService.addRanking(rankingInfo, totalCertifyCount); + + // then + Double resultDouble = + redisTemplate.opsForZSet().score("Ranking", rankingInfo); + + assertAll( + () -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(totalCertifyCount)) + ); + } + } + + @DisplayName("스코어 업데이트") + @Nested + class Update { + + @DisplayName("성공") + @Test + void update_success() { + // given + Long totalCertifyCount = 0L; + Member member = Member.builder() + .socialId("1") + .bug(BugFixture.bug()) + .build(); + member.increaseTotalCertifyCount(); + member.increaseTotalCertifyCount(); + Long expect = member.getTotalCertifyCount(); + CertifiedMemberInfo certifiedMemberInfo = CertifiedMemberInfo.builder() + .member(member) + .build(); + RankingInfo result = MemberMapper.toRankingInfo(member); + + // when + rankingService.addRanking(result, totalCertifyCount); + rankingService.updateCacheScore(certifiedMemberInfo); + Double resultDouble = + redisTemplate.opsForZSet().score("Ranking", result); + + // then + assertAll( + () -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(expect)) + ); + } + } + + @DisplayName("사용자 정보 변경") + @Nested + class Change { + + @DisplayName("성공") + @Test + void update_success() { + // given + Member member = Member.builder() + .socialId("1") + .bug(BugFixture.bug()) + .build(); + member.increaseTotalCertifyCount(); + member.increaseTotalCertifyCount(); + Long expect = member.getTotalCertifyCount(); + CertifiedMemberInfo certifiedMemberInfo = CertifiedMemberInfo.builder() + .member(member) + .build(); + RankingInfo before = MemberMapper.toRankingInfo(member); + + // when + rankingService.updateCacheScore(certifiedMemberInfo); + member.changeIntro("밥세공기"); + RankingInfo changeInfo = MemberMapper.toRankingInfo(member); + rankingService.changeInfos(before, changeInfo); + + Double resultDouble = + redisTemplate.opsForZSet().score("Ranking", changeInfo); + + // then + assertAll( + () -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(expect)) + ); + } + } + + @DisplayName("랭킹 삭제") + @Nested + class Delete { + + @DisplayName("성공") + @Test + void update_success() { + // given + Long totalCertify = 5L; + Member member = Member.builder() + .socialId("1") + .bug(BugFixture.bug()) + .build(); + member.increaseTotalCertifyCount(); + member.increaseTotalCertifyCount(); + RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); + + rankingService.addRanking(rankingInfo, totalCertify); + + // when + rankingService.removeRanking(rankingInfo); + + Double resultDouble = + redisTemplate.opsForZSet().score("Ranking", rankingInfo); + + // then + assertThat(resultDouble).isNull(); + } + } +} diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 4c19555c..8fd6e465 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Objects; import java.util.Optional; import org.assertj.core.api.Assertions; @@ -29,6 +30,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Import; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -45,6 +48,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.application.auth.OAuth2AuthorizationServerRequestService; import com.moabam.api.application.image.ImageService; +import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.image.ImageType; import com.moabam.api.domain.item.Inventory; @@ -63,6 +67,8 @@ import com.moabam.api.domain.room.repository.RoomRepository; import com.moabam.api.dto.auth.TokenSaveValue; import com.moabam.api.dto.member.ModifyMemberRequest; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.global.config.EmbeddedRedisConfig; import com.moabam.global.config.OAuthConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.handler.RestTemplateResponseHandler; @@ -83,6 +89,7 @@ @AutoConfigureMockMvc @AutoConfigureMockRestServiceServer @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@Import(EmbeddedRedisConfig.class) class MemberControllerTest extends WithoutFilterSupporter { @Autowired @@ -133,6 +140,9 @@ class MemberControllerTest extends WithoutFilterSupporter { @Autowired EntityManager entityManager; + @Autowired + RedisTemplate redisTemplate; + @BeforeAll void allSetUp() { restTemplateBuilder = new RestTemplateBuilder() @@ -481,6 +491,7 @@ void member_modify_request_success(String intro, String nickname) throws Excepti .characterEncoding("UTF-8")) .andExpect(status().is2xxSuccessful()) .andDo(print()); + } @DisplayName("회원 프로필없이 성공 ") @@ -499,6 +510,8 @@ void member_modify_no_image_request_success(String intro, String nickname) throw willThrow(NullPointerException.class) .given(imageService).uploadImages(any(), any()); + RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, member.getTotalCertifyCount()); // expected mockMvc.perform(multipart(HttpMethod.POST, "/members/modify") @@ -508,5 +521,15 @@ void member_modify_no_image_request_success(String intro, String nickname) throw .characterEncoding("UTF-8")) .andExpect(status().is2xxSuccessful()) .andDo(print()); + + String updateNick = member.getNickname(); + + if (Objects.nonNull(nickname)) { + updateNick = nickname; + } + + Double result = redisTemplate.opsForZSet() + .score("Ranking", new RankingInfo(member.getId(), updateNick, member.getProfileImage())); + assertThat(result).isEqualTo(member.getTotalCertifyCount()); } } diff --git a/src/test/java/com/moabam/support/fixture/RankingInfoFixture.java b/src/test/java/com/moabam/support/fixture/RankingInfoFixture.java new file mode 100644 index 00000000..9058ebcc --- /dev/null +++ b/src/test/java/com/moabam/support/fixture/RankingInfoFixture.java @@ -0,0 +1,5 @@ +package com.moabam.support.fixture; + +public class RankingInfoFixture { + +} From 51fe35335ae5dea66f049a3c0fd83a18a684c8c5 Mon Sep 17 00:00:00 2001 From: parksey Date: Wed, 29 Nov 2023 13:54:04 +0900 Subject: [PATCH 03/20] =?UTF-8?q?feat:=20=EB=9E=AD=ED=82=B9=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20API=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/member/MemberMapper.java | 10 ++ .../api/application/member/MemberService.java | 7 ++ .../application/ranking/RankingMapper.java | 43 +++++++++ .../application/ranking/RankingService.java | 51 ++++++++++- .../api/dto/ranking/PersonalRankingInfo.java | 13 +++ .../dto/ranking/TopRankingInfoResponse.java | 14 +++ .../api/dto/ranking/TopRankingResponses.java | 13 +++ .../redis/ZSetRedisRepository.java | 18 ++++ .../api/presentation/RankingController.java | 32 +++++++ .../global/config/EmbeddedRedisConfig.java | 6 ++ .../com/moabam/global/config/RedisConfig.java | 7 ++ .../ranking/RankingServiceTest.java | 91 +++++++++++++++++++ 12 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/moabam/api/application/ranking/RankingMapper.java create mode 100644 src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java create mode 100644 src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java create mode 100644 src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java create mode 100644 src/main/java/com/moabam/api/presentation/RankingController.java diff --git a/src/main/java/com/moabam/api/application/member/MemberMapper.java b/src/main/java/com/moabam/api/application/member/MemberMapper.java index 600f16f6..66d9baa4 100644 --- a/src/main/java/com/moabam/api/application/member/MemberMapper.java +++ b/src/main/java/com/moabam/api/application/member/MemberMapper.java @@ -19,6 +19,7 @@ import com.moabam.api.dto.member.MemberInfo; import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; +import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; import lombok.AccessLevel; @@ -99,4 +100,13 @@ public static RankingInfo toRankingInfo(Member member) { .image(member.getProfileImage()) .build(); } + + public static PersonalRankingInfo toRankingInfoWithScore(Member member) { + return PersonalRankingInfo.builder() + .score(member.getTotalCertifyCount()) + .memberId(member.getId()) + .nickname(member.getNickname()) + .image(member.getProfileImage()) + .build(); + } } diff --git a/src/main/java/com/moabam/api/application/member/MemberService.java b/src/main/java/com/moabam/api/application/member/MemberService.java index 8ecdec82..6d1c84bc 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -24,6 +24,7 @@ import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; import com.moabam.api.dto.member.ModifyMemberRequest; +import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.common.util.BaseDataCode; @@ -106,6 +107,12 @@ public void modifyInfo(AuthMember authMember, ModifyMemberRequest modifyMemberRe rankingService.changeInfos(beforeInfo, afterInfo); } + public PersonalRankingInfo getRankingInfo(AuthMember authMember) { + Member member = findMember(authMember.id()); + + return MemberMapper.toRankingInfoWithScore(member); + } + private void validateNickname(String nickname) { if (StringUtils.isEmpty(nickname) && memberRepository.existsByNickname(nickname)) { throw new ConflictException(NICKNAME_CONFLICT); diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java new file mode 100644 index 00000000..ad9c0924 --- /dev/null +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -0,0 +1,43 @@ +package com.moabam.api.application.ranking; + +import java.util.List; + +import com.moabam.api.dto.ranking.PersonalRankingInfo; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingResponses; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RankingMapper { + + public static TopRankingInfoResponse topRankingResponse(int rank, long score, RankingInfo rankInfo) { + return TopRankingInfoResponse.builder() + .rank(rank) + .score(score) + .nickname(rankInfo.nickname()) + .image(rankInfo.image()) + .memberId(rankInfo.memberId()) + .build(); + } + + public static TopRankingInfoResponse topRankingResponse(int rank, PersonalRankingInfo rankInfo) { + return TopRankingInfoResponse.builder() + .rank(rank) + .score(rankInfo.score()) + .nickname(rankInfo.nickname()) + .image(rankInfo.image()) + .memberId(rankInfo.memberId()) + .build(); + } + + public static TopRankingResponses topRankingResponses(TopRankingInfoResponse myRanking, + List topRankings) { + return TopRankingResponses.builder() + .topRankings(topRankings) + .myRanking(myRanking) + .build(); + } +} diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index 8460e210..075e44b8 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -1,10 +1,22 @@ package com.moabam.api.application.ranking; +import static java.util.Objects.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; +import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.member.Member; +import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingResponses; import com.moabam.api.dto.room.CertifiedMemberInfo; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; @@ -14,23 +26,54 @@ @RequiredArgsConstructor public class RankingService { + private static final String RANKING = "Ranking"; + private static final int START_INDEX = 0; + private static final int LIMIT_INDEX = 10; + + private final ObjectMapper objectMapper; private final ZSetRedisRepository zSetRedisRepository; public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { - zSetRedisRepository.add("Ranking", rankingInfo, totalCertifyCount); + zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); } public void updateCacheScore(CertifiedMemberInfo info) { Member member = info.member(); RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); - zSetRedisRepository.add("Ranking", rankingInfo, member.getTotalCertifyCount()); + zSetRedisRepository.add(RANKING, rankingInfo, member.getTotalCertifyCount()); } public void changeInfos(RankingInfo before, RankingInfo after) { - zSetRedisRepository.changeMember("Ranking", before, after); + zSetRedisRepository.changeMember(RANKING, before, after); } public void removeRanking(RankingInfo rankingInfo) { - zSetRedisRepository.delete("Ranking", rankingInfo); + zSetRedisRepository.delete(RANKING, rankingInfo); + } + + public TopRankingResponses getMemberRanking(PersonalRankingInfo myRankingInfo) { + List topRankings = getTopRankings(); + Long myRanking = zSetRedisRepository.rank("Ranking", myRankingInfo); + TopRankingInfoResponse myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking.intValue(), + myRankingInfo); + + return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); + } + + private List getTopRankings() { + Set> topRankings = + zSetRedisRepository.range(RANKING, START_INDEX, LIMIT_INDEX); + + Set scoreSet = new HashSet<>(); + List topRankingInfoRespons = new ArrayList<>(); + + for (ZSetOperations.TypedTuple topRanking : topRankings) { + long score = requireNonNull(topRanking.getScore()).longValue(); + scoreSet.add(score); + + RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); + topRankingInfoRespons.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + } + return topRankingInfoRespons; } } diff --git a/src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java b/src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java new file mode 100644 index 00000000..8a8ffc8d --- /dev/null +++ b/src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java @@ -0,0 +1,13 @@ +package com.moabam.api.dto.ranking; + +import lombok.Builder; + +@Builder +public record PersonalRankingInfo( + Long memberId, + String nickname, + String image, + Long score +) { + +} diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java new file mode 100644 index 00000000..ed0d3857 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java @@ -0,0 +1,14 @@ +package com.moabam.api.dto.ranking; + +import lombok.Builder; + +@Builder +public record TopRankingInfoResponse( + int rank, + Long memberId, + Long score, + String nickname, + String image +) { + +} diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java new file mode 100644 index 00000000..da1dd863 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java @@ -0,0 +1,13 @@ +package com.moabam.api.dto.ranking; + +import java.util.List; + +import lombok.Builder; + +@Builder +public record TopRankingResponses( + List topRankings, + TopRankingInfoResponse myRanking +) { + +} diff --git a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java index 5dce1e42..93934045 100644 --- a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java +++ b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java @@ -5,7 +5,9 @@ import java.util.Set; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; import org.springframework.data.redis.core.ZSetOperations.TypedTuple; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.stereotype.Repository; import lombok.RequiredArgsConstructor; @@ -48,4 +50,20 @@ public void changeMember(String key, Object before, Object after) { public void delete(String key, Object value) { redisTemplate.opsForZSet().remove(key, value); } + + public Set> range(String key, int startIndex, int limitIndex) { + setSerialize(Object.class); + Set> rankings = redisTemplate.opsForZSet() + .reverseRangeWithScores(key, startIndex, limitIndex); + setSerialize(String.class); + return rankings; + } + + private void setSerialize(Class classes) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(classes)); + } + + public Long rank(String key, Object myRankingInfo) { + return redisTemplate.opsForZSet().reverseRank(key, myRankingInfo); + } } diff --git a/src/main/java/com/moabam/api/presentation/RankingController.java b/src/main/java/com/moabam/api/presentation/RankingController.java new file mode 100644 index 00000000..9820454e --- /dev/null +++ b/src/main/java/com/moabam/api/presentation/RankingController.java @@ -0,0 +1,32 @@ +package com.moabam.api.presentation; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import com.moabam.api.application.member.MemberService; +import com.moabam.api.application.ranking.RankingService; +import com.moabam.api.dto.ranking.PersonalRankingInfo; +import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.global.auth.annotation.Auth; +import com.moabam.global.auth.model.AuthMember; + +import lombok.RequiredArgsConstructor; + +@RequestMapping("/ranking") +@RestController +@RequiredArgsConstructor +public class RankingController { + + private final RankingService rankingService; + private final MemberService memberService; + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public TopRankingResponses getRanking(@Auth AuthMember authMember) { + PersonalRankingInfo rankingInfo = memberService.getRankingInfo(authMember); + return rankingService.getMemberRanking(rankingInfo); + } +} diff --git a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java index 02268602..098109bc 100644 --- a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java +++ b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java @@ -17,6 +17,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.util.StringUtils; +import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.global.error.exception.MoabamException; import com.moabam.global.error.model.ErrorMessage; @@ -60,6 +61,11 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + public void startRedis() { Os os = Os.createOs(); availablePort = findPort(os); diff --git a/src/main/java/com/moabam/global/config/RedisConfig.java b/src/main/java/com/moabam/global/config/RedisConfig.java index 9d017cce..31105b5c 100644 --- a/src/main/java/com/moabam/global/config/RedisConfig.java +++ b/src/main/java/com/moabam/global/config/RedisConfig.java @@ -10,6 +10,8 @@ import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; + @Configuration @Profile("!test") public class RedisConfig { @@ -35,4 +37,9 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } } diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index 01b967dc..6c15a39f 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -3,15 +3,20 @@ import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; +import java.util.Set; + 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.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.member.Member; +import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.room.CertifiedMemberInfo; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; @@ -160,4 +165,90 @@ void update_success() { assertThat(resultDouble).isNull(); } } + + @DisplayName("조회") + @Nested + class Select { + + @DisplayName("성공") + @Test + void test() { + // given + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(1L, "Hello1", "123"), + 1); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(2L, "Hello2", "123"), + 2); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(3L, "Hello3", "123"), + 3); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(4L, "Hello4", "123"), + 4); + + // when + setSerialize(Object.class); + Set> rankings = redisTemplate.opsForZSet() + .reverseRangeWithScores("Ranking", 0, 2); + setSerialize(String.class); + + // then + assertThat(rankings).hasSize(3); + } + + @DisplayName("일부만 조회 성공") + @Test + void search_part() { + // given + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(1L, "Hello1", "123"), + 1); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(2L, "Hello2", "123"), + 2); + + // when + setSerialize(Object.class); + Set> rankings = redisTemplate.opsForZSet() + .reverseRangeWithScores("Ranking", 0, 10); + setSerialize(String.class); + + // then + assertThat(rankings).hasSize(2); + } + + private void setSerialize(Class classes) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(classes)); + } + + @DisplayName("랭킹 조회 성공") + @Test + void getTopRankings() { + // given + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(1L, "Hello1", "123"), + 1); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(2L, "Hello2", "123"), + 2); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(4L, "Hello3", "123"), + 2); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(3L, "Hello2", "123"), + 3); + redisTemplate.opsForZSet().add("Ranking", + new RankingInfo(5L, "Hello4", "123"), + 3); + + // When + Then + assertThatNoException().isThrownBy(() -> rankingService.getMemberRanking(PersonalRankingInfo.builder() + .score(1L) + .image("123") + .nickname("Hello1") + .memberId(1L) + .build())); + } + } } From 0ac854c49376ea57b81aba115e0298438f4ff284 Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 04:17:00 +0900 Subject: [PATCH 04/20] =?UTF-8?q?feat:=20=EB=9E=AD=ED=82=B9=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=9E=AD=ED=82=B9=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EA=B0=81=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20->=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A5=B4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/member/MemberMapper.java | 34 ++-- .../api/application/member/MemberService.java | 18 ++- .../notification/NotificationService.java | 4 +- .../application/ranking/RankingMapper.java | 12 +- .../application/ranking/RankingService.java | 23 ++- .../repository/MemberSearchRepository.java | 9 ++ ...nalRankingInfo.java => UpdateRanking.java} | 6 +- .../redis/ZSetRedisRepository.java | 8 +- .../api/presentation/RankingController.java | 7 +- .../api/presentation/RoomController.java | 3 - .../application/member/MemberServiceTest.java | 13 ++ .../ranking/RankingServiceTest.java | 149 ++++++++---------- .../CertificationServiceConcurrencyTest.java | 10 +- .../room/CertificationServiceTest.java | 6 +- .../room/RoomServiceConcurrencyTest.java | 8 +- .../api/application/room/RoomServiceTest.java | 2 +- .../InventorySearchRepositoryTest.java | 10 +- .../domain/member/MemberRepositoryTest.java | 10 +- .../presentation/MemberControllerTest.java | 6 +- .../NotificationControllerTest.java | 2 +- .../presentation/RankingControllerTest.java | 82 ++++++++++ .../presentation/ReportControllerTest.java | 4 +- .../api/presentation/RoomControllerTest.java | 14 +- .../moabam/support/fixture/MemberFixture.java | 2 +- 24 files changed, 261 insertions(+), 181 deletions(-) rename src/main/java/com/moabam/api/dto/ranking/{PersonalRankingInfo.java => UpdateRanking.java} (51%) create mode 100644 src/test/java/com/moabam/api/presentation/RankingControllerTest.java diff --git a/src/main/java/com/moabam/api/application/member/MemberMapper.java b/src/main/java/com/moabam/api/application/member/MemberMapper.java index 66d9baa4..67fd8a92 100644 --- a/src/main/java/com/moabam/api/application/member/MemberMapper.java +++ b/src/main/java/com/moabam/api/application/member/MemberMapper.java @@ -19,8 +19,8 @@ import com.moabam.api.dto.member.MemberInfo; import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -35,6 +35,13 @@ public static Member toMember(Long socialId) { .build(); } + public static UpdateRanking toUpdateRanking(Member member) { + return UpdateRanking.builder() + .rankingInfo(toRankingInfo(member)) + .score(member.getTotalCertifyCount()) + .build(); + } + public static MemberInfoSearchResponse toMemberInfoSearchResponse(List memberInfos) { MemberInfo infos = memberInfos.get(0); List badgeTypes = memberInfos.stream() @@ -81,6 +88,14 @@ public static Inventory toInventory(Long memberId, Item item) { .build(); } + public static RankingInfo toRankingInfo(Member member) { + return RankingInfo.builder() + .memberId(member.getId()) + .nickname(member.getNickname()) + .image(member.getProfileImage()) + .build(); + } + private static List badgedNames(Set badgeTypes) { return BadgeType.memberBadgeMap(badgeTypes); } @@ -92,21 +107,4 @@ private static Map defaultSkins(String morningImage, String nigh return birdsSkin; } - - public static RankingInfo toRankingInfo(Member member) { - return RankingInfo.builder() - .memberId(member.getId()) - .nickname(member.getNickname()) - .image(member.getProfileImage()) - .build(); - } - - public static PersonalRankingInfo toRankingInfoWithScore(Member member) { - return PersonalRankingInfo.builder() - .score(member.getTotalCertifyCount()) - .memberId(member.getId()) - .nickname(member.getNickname()) - .image(member.getProfileImage()) - .build(); - } } diff --git a/src/main/java/com/moabam/api/application/member/MemberService.java b/src/main/java/com/moabam/api/application/member/MemberService.java index 554033ed..7cfec537 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -6,6 +6,7 @@ import java.util.Objects; import java.util.Optional; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,8 +28,8 @@ import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; import com.moabam.api.dto.member.ModifyMemberRequest; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.common.util.BaseDataCode; import com.moabam.global.common.util.ClockHolder; @@ -124,10 +125,20 @@ public void modifyInfo(AuthMember authMember, ModifyMemberRequest modifyMemberRe } } - public PersonalRankingInfo getRankingInfo(AuthMember authMember) { + public UpdateRanking getRankingInfo(AuthMember authMember) { Member member = findMember(authMember.id()); - return MemberMapper.toRankingInfoWithScore(member); + return MemberMapper.toUpdateRanking(member); + } + + @Scheduled(cron = "* 15 * * * *") + public void updateAllRanking() { + List members = memberSearchRepository.findAllMembers(); + List updateRankings = members.stream() + .map(MemberMapper::toUpdateRanking) + .toList(); + + rankingService.updateScores(updateRankings); } private void changeNickname(Long memberId, String changedName) { @@ -138,7 +149,6 @@ private void changeNickname(Long memberId, String changedName) { } } - private void validateNickname(String nickname) { if (Objects.isNull(nickname)) { return; diff --git a/src/main/java/com/moabam/api/application/notification/NotificationService.java b/src/main/java/com/moabam/api/application/notification/NotificationService.java index 7eedd953..dccd2a03 100644 --- a/src/main/java/com/moabam/api/application/notification/NotificationService.java +++ b/src/main/java/com/moabam/api/application/notification/NotificationService.java @@ -73,8 +73,8 @@ public List getMyKnockStatusInRoom(Long memberId, Long roomId, List !participant.getMemberId().equals(memberId)) .toList(); - Predicate knockPredicate = targetId -> - notificationRepository.existsKnockByKey(memberId, targetId, roomId); + Predicate knockPredicate = targetId -> notificationRepository.existsKnockByKey(memberId, targetId, + roomId); Map> knockStatus = filteredParticipants.stream() .map(Participant::getMemberId) diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index ad9c0924..eae3b65c 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -2,10 +2,10 @@ import java.util.List; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfoResponse; import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.UpdateRanking; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -23,13 +23,13 @@ public static TopRankingInfoResponse topRankingResponse(int rank, long score, Ra .build(); } - public static TopRankingInfoResponse topRankingResponse(int rank, PersonalRankingInfo rankInfo) { + public static TopRankingInfoResponse topRankingResponse(int rank, UpdateRanking updateRanking) { return TopRankingInfoResponse.builder() .rank(rank) - .score(rankInfo.score()) - .nickname(rankInfo.nickname()) - .image(rankInfo.image()) - .memberId(rankInfo.memberId()) + .score(updateRanking.score()) + .nickname(updateRanking.rankingInfo().nickname()) + .image(updateRanking.rankingInfo().image()) + .memberId(updateRanking.rankingInfo().memberId()) .build(); } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index a4c6842c..fdcc8638 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -11,13 +11,10 @@ import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; -import com.moabam.api.application.member.MemberMapper; -import com.moabam.api.domain.member.Member; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfoResponse; import com.moabam.api.dto.ranking.TopRankingResponses; -import com.moabam.api.dto.room.CertifiedMemberInfo; +import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import lombok.RequiredArgsConstructor; @@ -28,7 +25,7 @@ public class RankingService { private static final String RANKING = "Ranking"; private static final int START_INDEX = 0; - private static final int LIMIT_INDEX = 10; + private static final int LIMIT_INDEX = 9; private final ObjectMapper objectMapper; private final ZSetRedisRepository zSetRedisRepository; @@ -37,10 +34,9 @@ public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); } - public void updateCacheScore(CertifiedMemberInfo info) { - Member member = info.member(); - RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); - zSetRedisRepository.add(RANKING, rankingInfo, member.getTotalCertifyCount()); + public void updateScores(List updateRankings) { + updateRankings.forEach(updateRanking -> + zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); } public void changeInfos(RankingInfo before, RankingInfo after) { @@ -51,11 +47,11 @@ public void removeRanking(RankingInfo rankingInfo) { zSetRedisRepository.delete(RANKING, rankingInfo); } - public TopRankingResponses getMemberRanking(PersonalRankingInfo myRankingInfo) { + public TopRankingResponses getMemberRanking(UpdateRanking myRankingInfo) { List topRankings = getTopRankings(); - Long myRanking = zSetRedisRepository.reverseRank("Ranking", myRankingInfo); - TopRankingInfoResponse myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking.intValue(), - myRankingInfo); + Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); + TopRankingInfoResponse myRankingInfoResponse = + RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); } @@ -74,6 +70,7 @@ private List getTopRankings() { RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); topRankingInfoRespons.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); } + return topRankingInfoRespons; } } diff --git a/src/main/java/com/moabam/api/domain/member/repository/MemberSearchRepository.java b/src/main/java/com/moabam/api/domain/member/repository/MemberSearchRepository.java index b853b871..82a43a01 100644 --- a/src/main/java/com/moabam/api/domain/member/repository/MemberSearchRepository.java +++ b/src/main/java/com/moabam/api/domain/member/repository/MemberSearchRepository.java @@ -29,6 +29,15 @@ public Optional findMember(Long memberId) { return findMember(memberId, true); } + public List findAllMembers() { + return jpaQueryFactory + .selectFrom(member) + .where( + member.deletedAt.isNotNull() + ) + .fetch(); + } + public Optional findMember(Long memberId, boolean isNotDeleted) { return Optional.ofNullable(jpaQueryFactory .selectFrom(member) diff --git a/src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java b/src/main/java/com/moabam/api/dto/ranking/UpdateRanking.java similarity index 51% rename from src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java rename to src/main/java/com/moabam/api/dto/ranking/UpdateRanking.java index 8a8ffc8d..6b5218ee 100644 --- a/src/main/java/com/moabam/api/dto/ranking/PersonalRankingInfo.java +++ b/src/main/java/com/moabam/api/dto/ranking/UpdateRanking.java @@ -3,10 +3,8 @@ import lombok.Builder; @Builder -public record PersonalRankingInfo( - Long memberId, - String nickname, - String image, +public record UpdateRanking( + RankingInfo rankingInfo, Long score ) { diff --git a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java index 4457a26a..d68e43b7 100644 --- a/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java +++ b/src/main/java/com/moabam/api/infrastructure/redis/ZSetRedisRepository.java @@ -68,11 +68,11 @@ public Set> rangeJson(String key, int startIndex, int limitIn return rankings; } - private void setSerialize(Class classes) { - redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(classes)); - } - public Long reverseRank(String key, Object myRankingInfo) { return redisTemplate.opsForZSet().reverseRank(key, myRankingInfo); } + + private void setSerialize(Class classes) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(classes)); + } } diff --git a/src/main/java/com/moabam/api/presentation/RankingController.java b/src/main/java/com/moabam/api/presentation/RankingController.java index 9820454e..68796632 100644 --- a/src/main/java/com/moabam/api/presentation/RankingController.java +++ b/src/main/java/com/moabam/api/presentation/RankingController.java @@ -8,14 +8,14 @@ import com.moabam.api.application.member.MemberService; import com.moabam.api.application.ranking.RankingService; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.global.auth.annotation.Auth; import com.moabam.global.auth.model.AuthMember; import lombok.RequiredArgsConstructor; -@RequestMapping("/ranking") +@RequestMapping("/rankings") @RestController @RequiredArgsConstructor public class RankingController { @@ -26,7 +26,8 @@ public class RankingController { @GetMapping @ResponseStatus(HttpStatus.OK) public TopRankingResponses getRanking(@Auth AuthMember authMember) { - PersonalRankingInfo rankingInfo = memberService.getRankingInfo(authMember); + UpdateRanking rankingInfo = memberService.getRankingInfo(authMember); + return rankingService.getMemberRanking(rankingInfo); } } diff --git a/src/main/java/com/moabam/api/presentation/RoomController.java b/src/main/java/com/moabam/api/presentation/RoomController.java index 142cfa80..2581e5e1 100644 --- a/src/main/java/com/moabam/api/presentation/RoomController.java +++ b/src/main/java/com/moabam/api/presentation/RoomController.java @@ -18,7 +18,6 @@ import org.springframework.web.multipart.MultipartFile; import com.moabam.api.application.image.ImageService; -import com.moabam.api.application.ranking.RankingService; import com.moabam.api.application.room.CertificationService; import com.moabam.api.application.room.RoomService; import com.moabam.api.application.room.SearchService; @@ -49,7 +48,6 @@ public class RoomController { private final SearchService searchService; private final CertificationService certificationService; private final ImageService imageService; - private final RankingService rankingService; @PostMapping @ResponseStatus(HttpStatus.CREATED) @@ -116,7 +114,6 @@ public void certifyRoom(@Auth AuthMember authMember, @PathVariable("roomId") Lon List imageUrls = imageService.uploadImages(multipartFiles, ImageType.CERTIFICATION); CertifiedMemberInfo info = certificationService.getCertifiedMemberInfo(authMember.id(), roomId, imageUrls); certificationService.certifyRoom(info); - rankingService.updateCacheScore(info); } @PutMapping("/{roomId}/members/{memberId}/mandate") diff --git a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java index 4969c93e..4be1fe70 100644 --- a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java +++ b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java @@ -229,4 +229,17 @@ void modify_success_test(@WithMember AuthMember authMember) { () -> assertThat(member.getProfileImage()).isEqualTo("/main") ); } + + @DisplayName("모든 랭킹 업데이트") + @Test + void update_all_ranking() { + // given + Member member1 = MemberFixture.member("1"); + Member member2 = MemberFixture.member("2"); + given(memberSearchRepository.findAllMembers()) + .willReturn(List.of(member1, member2)); + + // when + Then + assertThatNoException().isThrownBy(() -> memberService.updateAllRanking()); + } } diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index 6c15a39f..b33c2f9d 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; +import java.util.List; import java.util.Set; import org.junit.jupiter.api.DisplayName; @@ -16,12 +17,14 @@ import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.member.Member; -import com.moabam.api.dto.ranking.PersonalRankingInfo; import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.room.CertifiedMemberInfo; +import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import com.moabam.global.config.EmbeddedRedisConfig; import com.moabam.support.fixture.BugFixture; +import com.moabam.support.fixture.MemberFixture; @SpringBootTest(classes = {EmbeddedRedisConfig.class, RankingService.class, ZSetRedisRepository.class}) public class RankingServiceTest { @@ -54,13 +57,10 @@ void add_success() { rankingService.addRanking(rankingInfo, totalCertifyCount); // then - Double resultDouble = - redisTemplate.opsForZSet().score("Ranking", rankingInfo); + Double resultDouble = redisTemplate.opsForZSet().score("Ranking", rankingInfo); - assertAll( - () -> assertThat(resultDouble).isNotNull(), - () -> assertThat(resultDouble).isEqualTo(Double.valueOf(totalCertifyCount)) - ); + assertAll(() -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(totalCertifyCount))); } } @@ -72,30 +72,25 @@ class Update { @Test void update_success() { // given - Long totalCertifyCount = 0L; - Member member = Member.builder() - .socialId("1") - .bug(BugFixture.bug()) - .build(); + Member member = MemberFixture.member("1"); member.increaseTotalCertifyCount(); member.increaseTotalCertifyCount(); - Long expect = member.getTotalCertifyCount(); - CertifiedMemberInfo certifiedMemberInfo = CertifiedMemberInfo.builder() - .member(member) - .build(); - RankingInfo result = MemberMapper.toRankingInfo(member); + + Member member1 = MemberFixture.member("2"); + member1.increaseTotalCertifyCount(); + member1.increaseTotalCertifyCount(); + + List members = List.of(member, member1); + List updateRankings = members.stream().map(MemberMapper::toUpdateRanking).toList(); // when - rankingService.addRanking(result, totalCertifyCount); - rankingService.updateCacheScore(certifiedMemberInfo); - Double resultDouble = - redisTemplate.opsForZSet().score("Ranking", result); + rankingService.updateScores(updateRankings); + + Double resultDouble = redisTemplate.opsForZSet().score("Ranking", updateRankings.get(0).rankingInfo()); // then - assertAll( - () -> assertThat(resultDouble).isNotNull(), - () -> assertThat(resultDouble).isEqualTo(Double.valueOf(expect)) - ); + assertAll(() -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(member.getTotalCertifyCount()))); } } @@ -107,32 +102,24 @@ class Change { @Test void update_success() { // given - Member member = Member.builder() - .socialId("1") - .bug(BugFixture.bug()) - .build(); + Member member = Member.builder().socialId("1").bug(BugFixture.bug()).build(); member.increaseTotalCertifyCount(); member.increaseTotalCertifyCount(); - Long expect = member.getTotalCertifyCount(); - CertifiedMemberInfo certifiedMemberInfo = CertifiedMemberInfo.builder() - .member(member) - .build(); + + long expect = member.getTotalCertifyCount(); RankingInfo before = MemberMapper.toRankingInfo(member); + rankingService.addRanking(before, member.getTotalCertifyCount()); // when - rankingService.updateCacheScore(certifiedMemberInfo); member.changeIntro("밥세공기"); RankingInfo changeInfo = MemberMapper.toRankingInfo(member); rankingService.changeInfos(before, changeInfo); - Double resultDouble = - redisTemplate.opsForZSet().score("Ranking", changeInfo); + Double resultDouble = redisTemplate.opsForZSet().score("Ranking", changeInfo); // then - assertAll( - () -> assertThat(resultDouble).isNotNull(), - () -> assertThat(resultDouble).isEqualTo(Double.valueOf(expect)) - ); + assertAll(() -> assertThat(resultDouble).isNotNull(), + () -> assertThat(resultDouble).isEqualTo(Double.valueOf(expect))); } } @@ -145,10 +132,7 @@ class Delete { void update_success() { // given Long totalCertify = 5L; - Member member = Member.builder() - .socialId("1") - .bug(BugFixture.bug()) - .build(); + Member member = Member.builder().socialId("1").bug(BugFixture.bug()).build(); member.increaseTotalCertifyCount(); member.increaseTotalCertifyCount(); RankingInfo rankingInfo = MemberMapper.toRankingInfo(member); @@ -158,8 +142,7 @@ void update_success() { // when rankingService.removeRanking(rankingInfo); - Double resultDouble = - redisTemplate.opsForZSet().score("Ranking", rankingInfo); + Double resultDouble = redisTemplate.opsForZSet().score("Ranking", rankingInfo); // then assertThat(resultDouble).isNull(); @@ -174,18 +157,10 @@ class Select { @Test void test() { // given - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(1L, "Hello1", "123"), - 1); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(2L, "Hello2", "123"), - 2); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(3L, "Hello3", "123"), - 3); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(4L, "Hello4", "123"), - 4); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(1L, "Hello1", "123"), 1); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(2L, "Hello2", "123"), 2); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(3L, "Hello3", "123"), 3); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(4L, "Hello4", "123"), 4); // when setSerialize(Object.class); @@ -201,12 +176,8 @@ void test() { @Test void search_part() { // given - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(1L, "Hello1", "123"), - 1); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(2L, "Hello2", "123"), - 2); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(1L, "Hello1", "123"), 1); + redisTemplate.opsForZSet().add("Ranking", new RankingInfo(2L, "Hello2", "123"), 2); // when setSerialize(Object.class); @@ -226,29 +197,33 @@ private void setSerialize(Class classes) { @Test void getTopRankings() { // given - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(1L, "Hello1", "123"), - 1); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(2L, "Hello2", "123"), - 2); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(4L, "Hello3", "123"), - 2); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(3L, "Hello2", "123"), - 3); - redisTemplate.opsForZSet().add("Ranking", - new RankingInfo(5L, "Hello4", "123"), - 3); - - // When + Then - assertThatNoException().isThrownBy(() -> rankingService.getMemberRanking(PersonalRankingInfo.builder() + for (int i = 0; i < 20; i++) { + RankingInfo rankingInfo = new RankingInfo((long)(i + 1), "Hello" + (i + 1), "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); + } + RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); + RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); + + UpdateRanking myRanking = UpdateRanking.builder() .score(1L) - .image("123") - .nickname("Hello1") - .memberId(1L) - .build())); + .rankingInfo(RankingInfo.builder().nickname("Hello1").memberId(1L).image("123").build()) + .build(); + + // When + TopRankingResponses topRankingResponses = rankingService.getMemberRanking(myRanking); + + // Then + List topRankings = topRankingResponses.topRankings(); + TopRankingInfoResponse myRank = topRankingResponses.myRanking(); + assertAll(() -> assertThat(topRankings).hasSize(10), () -> assertThat(myRank.score()).isEqualTo(1), + () -> assertThat(topRankings.get(0).rank()).isEqualTo(1), + () -> assertThat(topRankings.get(1).rank()).isEqualTo(1), + () -> assertThat(topRankings.get(2).rank()).isEqualTo(2), + () -> assertThat(topRankings.get(3).rank()).isEqualTo(2), + () -> assertThat(topRankings.get(4).rank()).isEqualTo(3)); + } } } diff --git a/src/test/java/com/moabam/api/application/room/CertificationServiceConcurrencyTest.java b/src/test/java/com/moabam/api/application/room/CertificationServiceConcurrencyTest.java index fd1fdc1e..3edc5543 100644 --- a/src/test/java/com/moabam/api/application/room/CertificationServiceConcurrencyTest.java +++ b/src/test/java/com/moabam/api/application/room/CertificationServiceConcurrencyTest.java @@ -61,11 +61,11 @@ void certify_room_success() throws InterruptedException { } Room savedRoom = roomRepository.save(room); - Member member1 = MemberFixture.member("0000", "닉네임1"); - Member member2 = MemberFixture.member("1234", "닉네임2"); - Member member3 = MemberFixture.member("5678", "닉네임3"); - Member member4 = MemberFixture.member("3333", "닉네임4"); - Member member5 = MemberFixture.member("5555", "닉네임5"); + Member member1 = MemberFixture.member("0000"); + Member member2 = MemberFixture.member("1234"); + Member member3 = MemberFixture.member("5678"); + Member member4 = MemberFixture.member("3333"); + Member member5 = MemberFixture.member("5555"); List members = memberRepository.saveAll(List.of(member1, member2, member3, member4, member5)); diff --git a/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java b/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java index d15ecdf1..32b9eb9b 100644 --- a/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java +++ b/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java @@ -102,9 +102,9 @@ class CertificationServiceTest { void init() { room = spy(RoomFixture.room()); participant = spy(RoomFixture.participant(room, 1L)); - member1 = MemberFixture.member("1", "회원1"); - member2 = MemberFixture.member("2", "회원2"); - member3 = MemberFixture.member("3", "회원3"); + member1 = MemberFixture.member("1"); + member2 = MemberFixture.member("2"); + member3 = MemberFixture.member("3"); lenient().when(room.getId()).thenReturn(1L); lenient().when(participant.getRoom()).thenReturn(room); diff --git a/src/test/java/com/moabam/api/application/room/RoomServiceConcurrencyTest.java b/src/test/java/com/moabam/api/application/room/RoomServiceConcurrencyTest.java index 53a190f9..7e1c0f41 100644 --- a/src/test/java/com/moabam/api/application/room/RoomServiceConcurrencyTest.java +++ b/src/test/java/com/moabam/api/application/room/RoomServiceConcurrencyTest.java @@ -60,9 +60,9 @@ void enter_room_concurrency_test() throws InterruptedException { Room savedRoom = roomRepository.save(room); - Member member1 = MemberFixture.member("qwe", "닉네임1"); - Member member2 = MemberFixture.member("qwfe", "닉네임2"); - Member member3 = MemberFixture.member("qff", "닉네임3"); + Member member1 = MemberFixture.member("qwe"); + Member member2 = MemberFixture.member("qwfe"); + Member member3 = MemberFixture.member("qff"); memberRepository.saveAll(List.of(member1, member2, member3)); Participant participant1 = RoomFixture.participant(savedRoom, member1.getId()); @@ -79,7 +79,7 @@ void enter_room_concurrency_test() throws InterruptedException { // when for (int i = 0; i < threadCount; i++) { - Member member = MemberFixture.member(String.valueOf(i + 100), "test"); + Member member = MemberFixture.member(String.valueOf(i + 100)); newMembers.add(member); memberRepository.save(member); final Long memberId = member.getId(); diff --git a/src/test/java/com/moabam/api/application/room/RoomServiceTest.java b/src/test/java/com/moabam/api/application/room/RoomServiceTest.java index 667508f0..2a37acfc 100644 --- a/src/test/java/com/moabam/api/application/room/RoomServiceTest.java +++ b/src/test/java/com/moabam/api/application/room/RoomServiceTest.java @@ -115,7 +115,7 @@ void room_manager_mandate_success() { Long managerId = 1L; Long memberId = 2L; - Member member = MemberFixture.member("1234", "닉네임"); + Member member = MemberFixture.member("1234"); Room room = spy(RoomFixture.room()); given(room.getId()).willReturn(1L); diff --git a/src/test/java/com/moabam/api/domain/item/repository/InventorySearchRepositoryTest.java b/src/test/java/com/moabam/api/domain/item/repository/InventorySearchRepositoryTest.java index 1e6a3299..54063f6b 100644 --- a/src/test/java/com/moabam/api/domain/item/repository/InventorySearchRepositoryTest.java +++ b/src/test/java/com/moabam/api/domain/item/repository/InventorySearchRepositoryTest.java @@ -85,7 +85,7 @@ void empty_success() { @Test void find_one_success() { // given - Member member = memberRepository.save(member("999", "test")); + Member member = memberRepository.save(member("999")); Item item = itemRepository.save(nightMageSkin()); Inventory inventory = inventoryRepository.save(inventory(member.getId(), item)); @@ -100,7 +100,7 @@ void find_one_success() { @Test void find_default_success() { // given - Member member = memberRepository.save(member("11314", "test")); + Member member = memberRepository.save(member("11314")); Item item = itemRepository.save(nightMageSkin()); Inventory inventory = inventoryRepository.save(inventory(member.getId(), item)); inventory.select(); @@ -116,8 +116,8 @@ void find_default_success() { @Test void find_all_default_type_night_success() { // given - Member member1 = memberRepository.save(member("625", "회원1")); - Member member2 = memberRepository.save(member("255", "회원2")); + Member member1 = memberRepository.save(member("625")); + Member member2 = memberRepository.save(member("255")); Item item = itemRepository.save(nightMageSkin()); Inventory inventory1 = inventoryRepository.save(inventory(member1.getId(), item)); Inventory inventory2 = inventoryRepository.save(inventory(member2.getId(), item)); @@ -141,7 +141,7 @@ class FindDefaultBird { @Test void bird_find_success() { // given - Member member = MemberFixture.member("fffdd", "test"); + Member member = MemberFixture.member("fffdd"); member.exitRoom(RoomType.MORNING); memberRepository.save(member); diff --git a/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java b/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java index 8f600444..90729351 100644 --- a/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java +++ b/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java @@ -58,7 +58,7 @@ class MemberRepositoryTest { @Test void test() { // given - Member member = MemberFixture.member("313", "test"); + Member member = MemberFixture.member("313"); memberRepository.save(member); // when @@ -76,7 +76,7 @@ class FindMemberTest { @Test void room_exist_and_manager_error() { // given - Member member = MemberFixture.member("1111", "test"); + Member member = MemberFixture.member("1111"); memberRepository.save(member); Room room = RoomFixture.room(); @@ -102,7 +102,7 @@ void room_exist_and_not_manager_success() { room.changeManagerNickname("test"); roomRepository.save(room); - Member member = MemberFixture.member("44", "test"); + Member member = MemberFixture.member("44"); member.changeNickName("not"); memberRepository.save(member); @@ -133,7 +133,7 @@ void member_not_found() { @Test void search_info_success() { // given - Member member = MemberFixture.member("hhhh", "test"); + Member member = MemberFixture.member("hhhh"); member.enterRoom(RoomType.MORNING); memberRepository.save(member); @@ -158,7 +158,7 @@ void search_info_success() { @Test void no_badges_search_success() { // given - Member member = MemberFixture.member("ttttt", "test"); + Member member = MemberFixture.member("ttttt"); member.enterRoom(RoomType.MORNING); memberRepository.save(member); diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 41d42a6b..cb66bdd1 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -148,7 +148,7 @@ void allSetUp() { restTemplateBuilder = new RestTemplateBuilder() .errorHandler(new RestTemplateResponseHandler()); - member = MemberFixture.member("1234567890987654", "nickname"); + member = MemberFixture.member("1234567890987654"); member.increaseTotalCertifyCount(); memberRepository.save(member); } @@ -371,7 +371,7 @@ void search_my_info_with_no_badge_success() throws Exception { @Test void search_friend_info_success() throws Exception { // given - Member friend = MemberFixture.member("123456789", "nick"); + Member friend = MemberFixture.member("123456789"); memberRepository.save(friend); Badge morningBirth = BadgeFixture.badge(friend.getId(), BadgeType.MORNING_BIRTH); @@ -527,7 +527,7 @@ void member_modify_no_image_request_success(String intro, String nickname) throw if (Objects.nonNull(nickname)) { updateNick = nickname; } - + Double result = redisTemplate.opsForZSet() .score("Ranking", new RankingInfo(member.getId(), updateNick, member.getProfileImage())); assertThat(result).isEqualTo(member.getTotalCertifyCount()); diff --git a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java index 95581ddf..b8e3696c 100644 --- a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java @@ -73,7 +73,7 @@ class NotificationControllerTest extends WithoutFilterSupporter { @BeforeEach void setUp() { - target = memberRepository.save(MemberFixture.member("123", "targetName")); + target = memberRepository.save(MemberFixture.member("123")); room = roomRepository.save(RoomFixture.room()); knockKey = String.format("room_%s_member_%s_knocks_%s", room.getId(), 1, target.getId()); diff --git a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java new file mode 100644 index 00000000..9e124cb2 --- /dev/null +++ b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java @@ -0,0 +1,82 @@ +package com.moabam.api.presentation; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.transaction.annotation.Transactional; + +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.MemberFixture; + +@Transactional +@SpringBootTest +@AutoConfigureMockMvc +@AutoConfigureRestDocs +class RankingControllerTest extends WithoutFilterSupporter { + + @Autowired + MockMvc mockMvc; + + @Autowired + MemberRepository memberRepository; + + @Autowired + RedisTemplate redisTemplate; + + @DisplayName("") + @WithMember + @Test + void top_ranking() throws Exception { + // given + List members = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + Member member = MemberFixture.member(String.valueOf(i + 1)); + members.add(member); + + RankingInfo rankingInfo = new RankingInfo((long)(i + 1), member.getNickname(), member.getProfileImage()); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); + } + memberRepository.saveAll(members); + + RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); + RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); + + UpdateRanking myRanking = UpdateRanking.builder() + .score(1L) + .rankingInfo(RankingInfo.builder() + .nickname(members.get(0).getNickname()) + .memberId(members.get(0).getId()) + .image(members.get(0).getProfileImage()).build()) + .build(); + + // when + mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topRankings", hasSize(10))) + .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) + .andExpect(jsonPath("$.myRanking.rank", is(21))); + + // then + + } + +} diff --git a/src/test/java/com/moabam/api/presentation/ReportControllerTest.java b/src/test/java/com/moabam/api/presentation/ReportControllerTest.java index 006eb10a..4f5c688d 100644 --- a/src/test/java/com/moabam/api/presentation/ReportControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/ReportControllerTest.java @@ -110,7 +110,7 @@ void reports_success(boolean roomFilter, boolean certificationFilter) throws Exc @Test void reports_failBy_subject_null() throws Exception { // given - Member member = MemberFixture.member("2", "ji"); + Member member = MemberFixture.member("2"); memberRepository.save(member); ReportRequest reportRequest = ReportFixture.reportRequest(member.getId(), null, null); @@ -128,7 +128,7 @@ void reports_failBy_subject_null() throws Exception { @Test void reports_failBy_member() throws Exception { // given - Member newMember = MemberFixture.member("9999", "n"); + Member newMember = MemberFixture.member("9999"); memberRepository.save(newMember); newMember.delete(LocalDateTime.now()); diff --git a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java index 0577f001..9d6b965f 100644 --- a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java @@ -794,8 +794,8 @@ void get_room_details_test() throws Exception { Participant participant1 = RoomFixture.participant(room, 1L); participant1.enableManager(); - Member member2 = MemberFixture.member("2", "NICK2"); - Member member3 = MemberFixture.member("3", "NICK3"); + Member member2 = MemberFixture.member("2"); + Member member3 = MemberFixture.member("3"); roomRepository.save(room); routineRepository.saveAll(routines); @@ -860,7 +860,7 @@ void get_room_details_test() throws Exception { void deport_member_success() throws Exception { // given Room room = RoomFixture.room(); - Member member = MemberFixture.member("1234", "참여자"); + Member member = MemberFixture.member("1234"); memberRepository.save(member); Participant memberParticipant = RoomFixture.participant(room, member.getId()); @@ -911,7 +911,7 @@ void deport_self_fail() throws Exception { @Test void mandate_manager_success() throws Exception { // given - Member member2 = MemberFixture.member("1234", "방장될 멤버"); + Member member2 = MemberFixture.member("1234"); memberRepository.save(member2); Room room = RoomFixture.room(); @@ -1011,7 +1011,7 @@ void get_un_joined_room_details() throws Exception { Room room = RoomFixture.room("테스트 방", NIGHT, 21); Room savedRoom = roomRepository.save(room); - Member member1 = MemberFixture.member("901010", "testtest"); + Member member1 = MemberFixture.member("901010"); member1 = memberRepository.save(member1); Item item = ItemFixture.nightMageSkin(); @@ -1531,8 +1531,8 @@ void search_first_page_all_rooms_by_keyword_roomType_roomId_success() throws Exc @Test void get_room_details_before_modification_success() throws Exception { // given - Member member2 = MemberFixture.member("123", "참여자1"); - Member member3 = MemberFixture.member("456", "참여자2"); + Member member2 = MemberFixture.member("123"); + Member member3 = MemberFixture.member("456"); member2 = memberRepository.save(member2); member3 = memberRepository.save(member3); diff --git a/src/test/java/com/moabam/support/fixture/MemberFixture.java b/src/test/java/com/moabam/support/fixture/MemberFixture.java index 3bf73d15..c0ed7551 100644 --- a/src/test/java/com/moabam/support/fixture/MemberFixture.java +++ b/src/test/java/com/moabam/support/fixture/MemberFixture.java @@ -30,7 +30,7 @@ public static Member member(Bug bug) { .build(); } - public static Member member(String socialId, String nickname) { + public static Member member(String socialId) { return Member.builder() .socialId(socialId) .bug(BugFixture.bug()) From f585949696323186be62a7a3347f43ea926c4244 Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 12:48:50 +0900 Subject: [PATCH 05/20] =?UTF-8?q?style:=20checkstyle=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java | 2 +- .../com/moabam/api/application/ranking/RankingServiceTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java index ce82d3c1..a823fd26 100644 --- a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java +++ b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java @@ -68,7 +68,7 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModules(new JavaTimeModule()); - + return objectMapper; } diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index b33c2f9d..9a3f3204 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -210,7 +210,6 @@ void getTopRankings() { .score(1L) .rankingInfo(RankingInfo.builder().nickname("Hello1").memberId(1L).image("123").build()) .build(); - // When TopRankingResponses topRankingResponses = rankingService.getMemberRanking(myRanking); From f5783bb55cc84395d084e8cab9c306617cfbb4d3 Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 14:15:46 +0900 Subject: [PATCH 06/20] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=AA=85=20=EB=B3=80=EA=B2=BD=20TopRankingIn?= =?UTF-8?q?foResponse=20->=20TopRankingInfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/ranking/RankingMapper.java | 14 +++++++------- .../api/application/ranking/RankingService.java | 14 +++++++------- ...ankingInfoResponse.java => TopRankingInfo.java} | 2 +- .../api/dto/ranking/TopRankingResponses.java | 4 ++-- .../application/ranking/RankingServiceTest.java | 6 +++--- 5 files changed, 20 insertions(+), 20 deletions(-) rename src/main/java/com/moabam/api/dto/ranking/{TopRankingInfoResponse.java => TopRankingInfo.java} (79%) diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index eae3b65c..7dcef586 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -3,7 +3,7 @@ import java.util.List; import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponses; import com.moabam.api.dto.ranking.UpdateRanking; @@ -13,8 +13,8 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RankingMapper { - public static TopRankingInfoResponse topRankingResponse(int rank, long score, RankingInfo rankInfo) { - return TopRankingInfoResponse.builder() + public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { + return TopRankingInfo.builder() .rank(rank) .score(score) .nickname(rankInfo.nickname()) @@ -23,8 +23,8 @@ public static TopRankingInfoResponse topRankingResponse(int rank, long score, Ra .build(); } - public static TopRankingInfoResponse topRankingResponse(int rank, UpdateRanking updateRanking) { - return TopRankingInfoResponse.builder() + public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { + return TopRankingInfo.builder() .rank(rank) .score(updateRanking.score()) .nickname(updateRanking.rankingInfo().nickname()) @@ -33,8 +33,8 @@ public static TopRankingInfoResponse topRankingResponse(int rank, UpdateRanking .build(); } - public static TopRankingResponses topRankingResponses(TopRankingInfoResponse myRanking, - List topRankings) { + public static TopRankingResponses topRankingResponses(TopRankingInfo myRanking, + List topRankings) { return TopRankingResponses.builder() .topRankings(topRankings) .myRanking(myRanking) diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index ebe344f2..f784b84b 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -12,7 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponses; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; @@ -48,29 +48,29 @@ public void removeRanking(RankingInfo rankingInfo) { } public TopRankingResponses getMemberRanking(UpdateRanking myRankingInfo) { - List topRankings = getTopRankings(); + List topRankings = getTopRankings(); Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); - TopRankingInfoResponse myRankingInfoResponse = + TopRankingInfo myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); } - private List getTopRankings() { + private List getTopRankings() { Set> topRankings = zSetRedisRepository.rangeJson(RANKING, START_INDEX, LIMIT_INDEX); Set scoreSet = new HashSet<>(); - List topRankingInfoRespons = new ArrayList<>(); + List topRankingInfo = new ArrayList<>(); for (ZSetOperations.TypedTuple topRanking : topRankings) { long score = requireNonNull(topRanking.getScore()).longValue(); scoreSet.add(score); RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); - topRankingInfoRespons.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); } - return topRankingInfoRespons; + return topRankingInfo; } } diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingInfo.java similarity index 79% rename from src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java rename to src/main/java/com/moabam/api/dto/ranking/TopRankingInfo.java index ed0d3857..bcd56ff2 100644 --- a/src/main/java/com/moabam/api/dto/ranking/TopRankingInfoResponse.java +++ b/src/main/java/com/moabam/api/dto/ranking/TopRankingInfo.java @@ -3,7 +3,7 @@ import lombok.Builder; @Builder -public record TopRankingInfoResponse( +public record TopRankingInfo( int rank, Long memberId, Long score, diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java index da1dd863..a5644c7f 100644 --- a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java +++ b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java @@ -6,8 +6,8 @@ @Builder public record TopRankingResponses( - List topRankings, - TopRankingInfoResponse myRanking + List topRankings, + TopRankingInfo myRanking ) { } diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index 9a3f3204..d433aaa3 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -18,7 +18,7 @@ import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.member.Member; import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.TopRankingInfoResponse; +import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponses; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; @@ -214,8 +214,8 @@ void getTopRankings() { TopRankingResponses topRankingResponses = rankingService.getMemberRanking(myRanking); // Then - List topRankings = topRankingResponses.topRankings(); - TopRankingInfoResponse myRank = topRankingResponses.myRanking(); + List topRankings = topRankingResponses.topRankings(); + TopRankingInfo myRank = topRankingResponses.myRanking(); assertAll(() -> assertThat(topRankings).hasSize(10), () -> assertThat(myRank.score()).isEqualTo(1), () -> assertThat(topRankings.get(0).rank()).isEqualTo(1), () -> assertThat(topRankings.get(1).rank()).isEqualTo(1), From b0c12ca4d3cfe2b1c1e5bfacc0511e2772bd26ac Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 14:19:24 +0900 Subject: [PATCH 07/20] =?UTF-8?q?fix:=20=EB=9E=AD=ED=82=B9=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EC=8B=9C=EA=B0=84=2015=EB=B6=84?= =?UTF-8?q?=20=EB=A7=A4=EC=B4=88=EB=A7=88=EB=8B=A4=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=B0=A9=EC=8B=9D=20->=2015=EB=B6=84?= =?UTF-8?q?=EC=97=90=20=ED=95=9C=20=EB=B2=88=EB=A7=8C=20=EC=8B=A4=ED=96=89?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/moabam/api/application/member/MemberService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/moabam/api/application/member/MemberService.java b/src/main/java/com/moabam/api/application/member/MemberService.java index f4fc0ec0..358cf2df 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -134,7 +134,7 @@ public UpdateRanking getRankingInfo(AuthMember authMember) { return MemberMapper.toUpdateRanking(member); } - @Scheduled(cron = "* 15 * * * *") + @Scheduled(cron = "0 15 * * * *") public void updateAllRanking() { List members = memberSearchRepository.findAllMembers(); List updateRankings = members.stream() From 286820bfed2b05fe75923ec67a309bb661fb1186 Mon Sep 17 00:00:00 2001 From: Park Seyeon Date: Thu, 30 Nov 2023 16:13:56 +0900 Subject: [PATCH 08/20] =?UTF-8?q?refactor:=20=EB=9E=AD=ED=82=B9=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EB=B0=98=ED=99=98=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=A9=B4=20s=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kim Heebin --- .../java/com/moabam/api/dto/ranking/TopRankingResponses.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java index a5644c7f..38663842 100644 --- a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java +++ b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java @@ -5,7 +5,7 @@ import lombok.Builder; @Builder -public record TopRankingResponses( +public record TopRankingResponse( List topRankings, TopRankingInfo myRanking ) { From 431c95b6f2b973597d8ba86c13b08a01a31c7729 Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 16:17:01 +0900 Subject: [PATCH 09/20] =?UTF-8?q?refactor:=20ToprankingResponses=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EA=B0=9D=EC=B2=B4=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EB=AA=85=20TopRankingResponse=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moabam/api/application/ranking/RankingMapper.java | 6 +++--- .../moabam/api/application/ranking/RankingService.java | 4 ++-- .../{TopRankingResponses.java => TopRankingResponse.java} | 0 .../com/moabam/api/presentation/RankingController.java | 4 ++-- .../api/application/ranking/RankingServiceTest.java | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) rename src/main/java/com/moabam/api/dto/ranking/{TopRankingResponses.java => TopRankingResponse.java} (100%) diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index 7dcef586..06ad3389 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -4,7 +4,7 @@ import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; -import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import lombok.AccessLevel; @@ -33,9 +33,9 @@ public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRa .build(); } - public static TopRankingResponses topRankingResponses(TopRankingInfo myRanking, + public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, List topRankings) { - return TopRankingResponses.builder() + return TopRankingResponse.builder() .topRankings(topRankings) .myRanking(myRanking) .build(); diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index f784b84b..aa48370d 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -13,7 +13,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; -import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; @@ -47,7 +47,7 @@ public void removeRanking(RankingInfo rankingInfo) { zSetRedisRepository.delete(RANKING, rankingInfo); } - public TopRankingResponses getMemberRanking(UpdateRanking myRankingInfo) { + public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { List topRankings = getTopRankings(); Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); TopRankingInfo myRankingInfoResponse = diff --git a/src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java b/src/main/java/com/moabam/api/dto/ranking/TopRankingResponse.java similarity index 100% rename from src/main/java/com/moabam/api/dto/ranking/TopRankingResponses.java rename to src/main/java/com/moabam/api/dto/ranking/TopRankingResponse.java diff --git a/src/main/java/com/moabam/api/presentation/RankingController.java b/src/main/java/com/moabam/api/presentation/RankingController.java index 68796632..d218ca5d 100644 --- a/src/main/java/com/moabam/api/presentation/RankingController.java +++ b/src/main/java/com/moabam/api/presentation/RankingController.java @@ -8,7 +8,7 @@ import com.moabam.api.application.member.MemberService; import com.moabam.api.application.ranking.RankingService; -import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.global.auth.annotation.Auth; import com.moabam.global.auth.model.AuthMember; @@ -25,7 +25,7 @@ public class RankingController { @GetMapping @ResponseStatus(HttpStatus.OK) - public TopRankingResponses getRanking(@Auth AuthMember authMember) { + public TopRankingResponse getRanking(@Auth AuthMember authMember) { UpdateRanking rankingInfo = memberService.getRankingInfo(authMember); return rankingService.getMemberRanking(rankingInfo); diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index d433aaa3..d246c5df 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -19,7 +19,7 @@ import com.moabam.api.domain.member.Member; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; -import com.moabam.api.dto.ranking.TopRankingResponses; +import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import com.moabam.global.config.EmbeddedRedisConfig; @@ -211,11 +211,11 @@ void getTopRankings() { .rankingInfo(RankingInfo.builder().nickname("Hello1").memberId(1L).image("123").build()) .build(); // When - TopRankingResponses topRankingResponses = rankingService.getMemberRanking(myRanking); + TopRankingResponse topRankingResponse = rankingService.getMemberRanking(myRanking); // Then - List topRankings = topRankingResponses.topRankings(); - TopRankingInfo myRank = topRankingResponses.myRanking(); + List topRankings = topRankingResponse.topRankings(); + TopRankingInfo myRank = topRankingResponse.myRanking(); assertAll(() -> assertThat(topRankings).hasSize(10), () -> assertThat(myRank.score()).isEqualTo(1), () -> assertThat(topRankings.get(0).rank()).isEqualTo(1), () -> assertThat(topRankings.get(1).rank()).isEqualTo(1), From 2b8f7217656cdd03e77966190b9f8b9408c71e2b Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 19:01:10 +0900 Subject: [PATCH 10/20] =?UTF-8?q?fix:=20ObjectMapper=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moabam/global/config/EmbeddedRedisConfig.java | 4 +++- src/main/java/com/moabam/global/config/RedisConfig.java | 8 -------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java index a823fd26..85e7f64a 100644 --- a/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java +++ b/src/main/java/com/moabam/global/config/EmbeddedRedisConfig.java @@ -9,6 +9,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; @@ -64,8 +65,9 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } + @Order(2) @Bean - public ObjectMapper objectMapper() { + public ObjectMapper objectRedisMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModules(new JavaTimeModule()); diff --git a/src/main/java/com/moabam/global/config/RedisConfig.java b/src/main/java/com/moabam/global/config/RedisConfig.java index cc412271..0e9cf0fb 100644 --- a/src/main/java/com/moabam/global/config/RedisConfig.java +++ b/src/main/java/com/moabam/global/config/RedisConfig.java @@ -38,12 +38,4 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } - - @Bean - public ObjectMapper objectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModules(new JavaTimeModule()); - - return objectMapper; - } } From 3c995d4d328838caa2ba3c0842e93c73456074a7 Mon Sep 17 00:00:00 2001 From: parksey Date: Thu, 30 Nov 2023 19:19:33 +0900 Subject: [PATCH 11/20] =?UTF-8?q?fix:=20objectMapper=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/moabam/global/config/RedisConfig.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/java/com/moabam/global/config/RedisConfig.java b/src/main/java/com/moabam/global/config/RedisConfig.java index cc412271..9d017cce 100644 --- a/src/main/java/com/moabam/global/config/RedisConfig.java +++ b/src/main/java/com/moabam/global/config/RedisConfig.java @@ -10,9 +10,6 @@ import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - @Configuration @Profile("!test") public class RedisConfig { @@ -38,12 +35,4 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } - - @Bean - public ObjectMapper objectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModules(new JavaTimeModule()); - - return objectMapper; - } } From 89aef34bce2f82e79ee5c00738cc7a332450c3fc Mon Sep 17 00:00:00 2001 From: parksey Date: Fri, 1 Dec 2023 19:26:39 +0900 Subject: [PATCH 12/20] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/application/admin/AdminMapper.java | 12 ++++ .../admin/application/admin/AdminService.java | 56 +++++++++++++++++++ .../com/moabam/admin/domain/admin/Admin.java | 53 ++++++++++++++++++ .../admin/domain/admin/AdminRepository.java | 10 ++++ .../presentation/admin/AdminController.java | 39 +++++++++++++ .../auth/AuthorizationService.java | 14 +++-- .../application/auth/mapper/AuthMapper.java | 12 ++++ .../auth/mapper/AuthorizationMapper.java | 5 +- .../moabam/global/auth/filter/CorsFilter.java | 21 ++++--- .../global/config/AllowOriginConfig.java | 12 ++++ .../com/moabam/global/config/OAuthConfig.java | 3 +- .../com/moabam/global/config/WebConfig.java | 1 + .../handler/RestTemplateResponseHandler.java | 20 ++++++- .../global/error/model/ErrorMessage.java | 1 + src/main/resources/config | 2 +- .../auth/AuthorizationServiceTest.java | 12 ++-- .../ranking/RankingServiceTest.java | 6 ++ .../MemberAuthorizeControllerTest.java | 2 +- .../presentation/RankingControllerTest.java | 6 ++ .../common/WithoutFilterSupporter.java | 2 +- 20 files changed, 265 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/moabam/admin/application/admin/AdminMapper.java create mode 100644 src/main/java/com/moabam/admin/application/admin/AdminService.java create mode 100644 src/main/java/com/moabam/admin/domain/admin/Admin.java create mode 100644 src/main/java/com/moabam/admin/domain/admin/AdminRepository.java create mode 100644 src/main/java/com/moabam/admin/presentation/admin/AdminController.java create mode 100644 src/main/java/com/moabam/global/config/AllowOriginConfig.java diff --git a/src/main/java/com/moabam/admin/application/admin/AdminMapper.java b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java new file mode 100644 index 00000000..d9cf0b35 --- /dev/null +++ b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java @@ -0,0 +1,12 @@ +package com.moabam.admin.application.admin; + +import com.moabam.admin.domain.admin.Admin; + +public class AdminMapper { + + public static Admin toAdmin(Long socialId) { + return Admin.builder() + .socialId(String.valueOf(socialId)) + .build(); + } +} diff --git a/src/main/java/com/moabam/admin/application/admin/AdminService.java b/src/main/java/com/moabam/admin/application/admin/AdminService.java new file mode 100644 index 00000000..892f15cf --- /dev/null +++ b/src/main/java/com/moabam/admin/application/admin/AdminService.java @@ -0,0 +1,56 @@ +package com.moabam.admin.application.admin; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.moabam.admin.domain.admin.Admin; +import com.moabam.admin.domain.admin.AdminRepository; +import com.moabam.api.application.auth.AuthorizationService; +import com.moabam.api.application.auth.mapper.AuthMapper; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.LoginResponse; +import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.model.ErrorMessage; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class AdminService { + + @Value("${admin}") + private String adminLoginKey; + + private final AuthorizationService authorizationService; + private final AdminRepository adminRepository; + + public void validate(String state) { + if (!adminLoginKey.equals(state)) { + throw new BadRequestException(ErrorMessage.LOGIN_FAILED_ADMIN_KEY); + } + } + + public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, + AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + LoginResponse loginResponse = login(authorizationTokenInfoResponse); + authorizationService.issueServiceToken(httpServletResponse, loginResponse.publicClaim()); + + return loginResponse; + } + + private LoginResponse login(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + Optional admin = adminRepository.findBySocialId(String.valueOf(authorizationTokenInfoResponse.id())); + Admin loginMember = admin.orElseGet(() -> signUp(authorizationTokenInfoResponse.id())); + + return AuthMapper.toLoginResponse(loginMember, admin.isEmpty()); + } + + private Admin signUp(Long socialId) { + Admin admin = AdminMapper.toAdmin(socialId); + + return adminRepository.save(admin); + } +} diff --git a/src/main/java/com/moabam/admin/domain/admin/Admin.java b/src/main/java/com/moabam/admin/domain/admin/Admin.java new file mode 100644 index 00000000..d63cbeff --- /dev/null +++ b/src/main/java/com/moabam/admin/domain/admin/Admin.java @@ -0,0 +1,53 @@ +package com.moabam.admin.domain.admin; + +import static com.moabam.global.common.util.RandomUtils.*; +import static java.util.Objects.*; + +import org.hibernate.annotations.ColumnDefault; + +import com.moabam.api.domain.member.Role; +import com.moabam.global.common.entity.BaseTimeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Admin extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "nickname", unique = true) + private String nickname; + + @Column(name = "social_id", nullable = false, unique = true) + private String socialId; + + @Enumerated(EnumType.STRING) + @Column(name = "role", nullable = false) + @ColumnDefault("'USER'") + private Role role; + + @Builder + private Admin(String socialId) { + this.socialId = requireNonNull(socialId); + this.nickname = createNickName(); + this.role = Role.ADMIN; + } + + private String createNickName() { + return "오목눈이#" + randomStringValues(); + } +} diff --git a/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java b/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java new file mode 100644 index 00000000..eb3b37f0 --- /dev/null +++ b/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java @@ -0,0 +1,10 @@ +package com.moabam.admin.domain.admin; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AdminRepository extends JpaRepository { + + Optional findBySocialId(String s); +} diff --git a/src/main/java/com/moabam/admin/presentation/admin/AdminController.java b/src/main/java/com/moabam/admin/presentation/admin/AdminController.java new file mode 100644 index 00000000..8ef4354b --- /dev/null +++ b/src/main/java/com/moabam/admin/presentation/admin/AdminController.java @@ -0,0 +1,39 @@ +package com.moabam.admin.presentation.admin; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import com.moabam.admin.application.admin.AdminService; +import com.moabam.api.application.auth.AuthorizationService; +import com.moabam.api.dto.auth.AuthorizationCodeResponse; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.api.dto.auth.LoginResponse; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/admins") +@RequiredArgsConstructor +public class AdminController { + + private final AuthorizationService authorizationService; + private final AdminService adminService; + + @PostMapping("/login/kakao/oauth") + @ResponseStatus(HttpStatus.OK) + public LoginResponse authorizationTokenIssue(@RequestBody AuthorizationCodeResponse authorizationCodeResponse, + HttpServletResponse httpServletResponse) { + adminService.validate(authorizationCodeResponse.state()); + AuthorizationTokenResponse tokenResponse = authorizationService.requestAdminToken(authorizationCodeResponse); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = authorizationService.requestTokenInfo( + tokenResponse); + + return adminService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + } +} diff --git a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java index 34234f94..a2db2e9f 100644 --- a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java +++ b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java @@ -56,10 +56,17 @@ public void redirectToLoginPage(HttpServletResponse httpServletResponse) { oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); } + public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), + oAuthConfig.provider().adminRedirectUri()); + } + public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { validAuthorizationGrant(authorizationCodeResponse.code()); - return issueTokenToAuthorizationServer(authorizationCodeResponse.code()); + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); } public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { @@ -182,10 +189,9 @@ private void validAuthorizationGrant(String code) { } } - private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code) { + private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oAuthConfig, - code); + oAuthConfig, code, redirectUri); MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); ResponseEntity authorizationTokenResponse = oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(), diff --git a/src/main/java/com/moabam/api/application/auth/mapper/AuthMapper.java b/src/main/java/com/moabam/api/application/auth/mapper/AuthMapper.java index 7b56ae24..0fe48a56 100644 --- a/src/main/java/com/moabam/api/application/auth/mapper/AuthMapper.java +++ b/src/main/java/com/moabam/api/application/auth/mapper/AuthMapper.java @@ -1,5 +1,6 @@ package com.moabam.api.application.auth.mapper; +import com.moabam.admin.domain.admin.Admin; import com.moabam.api.domain.member.Member; import com.moabam.api.dto.auth.LoginResponse; import com.moabam.api.dto.auth.TokenSaveValue; @@ -22,6 +23,17 @@ public static LoginResponse toLoginResponse(Member member, boolean isSignUp) { .build(); } + public static LoginResponse toLoginResponse(Admin admin, boolean isSignUp) { + return LoginResponse.builder() + .publicClaim(PublicClaim.builder() + .id(admin.getId()) + .nickname(admin.getNickname()) + .role(admin.getRole()) + .build()) + .isSignUp(isSignUp) + .build(); + } + public static TokenSaveValue toTokenSaveValue(String refreshToken, String ip) { return TokenSaveValue.builder() .refreshToken(refreshToken) diff --git a/src/main/java/com/moabam/api/application/auth/mapper/AuthorizationMapper.java b/src/main/java/com/moabam/api/application/auth/mapper/AuthorizationMapper.java index cd06461d..3e566153 100644 --- a/src/main/java/com/moabam/api/application/auth/mapper/AuthorizationMapper.java +++ b/src/main/java/com/moabam/api/application/auth/mapper/AuthorizationMapper.java @@ -23,11 +23,12 @@ public static AuthorizationCodeRequest toAuthorizationCodeRequest(OAuthConfig oA .build(); } - public static AuthorizationTokenRequest toAuthorizationTokenRequest(OAuthConfig oAuthConfig, String code) { + public static AuthorizationTokenRequest toAuthorizationTokenRequest(OAuthConfig oAuthConfig, String code, + String redirectUri) { return AuthorizationTokenRequest.builder() .grantType(oAuthConfig.client().authorizationGrantType()) .clientId(oAuthConfig.client().clientId()) - .redirectUri(oAuthConfig.provider().redirectUri()) + .redirectUri(redirectUri) .code(code) .clientSecret(oAuthConfig.client().clientSecret()) .build(); diff --git a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java index c113f156..f363c2df 100644 --- a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java @@ -1,14 +1,15 @@ package com.moabam.global.auth.filter; import java.io.IOException; +import java.util.Objects; -import org.springframework.beans.factory.annotation.Value; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.HandlerExceptionResolver; import com.google.cloud.storage.HttpMethod; +import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; @@ -31,26 +32,27 @@ public class CorsFilter extends OncePerRequestFilter { private final HandlerExceptionResolver handlerExceptionResolver; - @Value("${allow}") - private String allowOrigin; + private final AllowOriginConfig allowOriginsConfig; @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { + String refer = httpServletRequest.getHeader("referer"); + String origin = secureMatch(refer); try { - if (!secureMatch(httpServletRequest, allowOrigin)) { + if (Objects.isNull(origin)) { throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); } } catch (UnauthorizedException unauthorizedException) { - log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOrigin); + log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, unauthorizedException); return; } - httpServletResponse.setHeader("Access-Control-Allow-Origin", allowOrigin); + httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); @@ -63,8 +65,11 @@ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServl filterChain.doFilter(httpServletRequest, httpServletResponse); } - public boolean secureMatch(HttpServletRequest request, String origin) { - return request.getHeader("referer").contains(origin); + public String secureMatch(String refer) { + return allowOriginsConfig.origin().stream() + .filter(refer::contains) + .findFirst() + .orElse(null); } public boolean isOption(String method) { diff --git a/src/main/java/com/moabam/global/config/AllowOriginConfig.java b/src/main/java/com/moabam/global/config/AllowOriginConfig.java new file mode 100644 index 00000000..d2ae8db6 --- /dev/null +++ b/src/main/java/com/moabam/global/config/AllowOriginConfig.java @@ -0,0 +1,12 @@ +package com.moabam.global.config; + +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "allows") +public record AllowOriginConfig( + List origin +) { + +} diff --git a/src/main/java/com/moabam/global/config/OAuthConfig.java b/src/main/java/com/moabam/global/config/OAuthConfig.java index 2a8eb592..4884aacb 100644 --- a/src/main/java/com/moabam/global/config/OAuthConfig.java +++ b/src/main/java/com/moabam/global/config/OAuthConfig.java @@ -26,7 +26,8 @@ public record Provider( String redirectUri, String tokenUri, String tokenInfo, - String unlink + String unlink, + String adminRedirectUri ) { } diff --git a/src/main/java/com/moabam/global/config/WebConfig.java b/src/main/java/com/moabam/global/config/WebConfig.java index 387714cb..27335b6d 100644 --- a/src/main/java/com/moabam/global/config/WebConfig.java +++ b/src/main/java/com/moabam/global/config/WebConfig.java @@ -35,6 +35,7 @@ public PathResolver pathResolver() { PathMapper.pathWithMethod("/members", List.of(HttpMethod.POST)), PathMapper.pathWithMethod("/members/login/oauth", List.of(HttpMethod.GET)), PathMapper.parsePath("/members/login/*/oauth"), + PathMapper.parsePath("/admins/login/*/oauth"), PathMapper.parsePath("/css/*"), PathMapper.parsePath("/js/*"), PathMapper.parsePath("/images/*"), diff --git a/src/main/java/com/moabam/global/error/handler/RestTemplateResponseHandler.java b/src/main/java/com/moabam/global/error/handler/RestTemplateResponseHandler.java index c234dbf1..d0708d97 100644 --- a/src/main/java/com/moabam/global/error/handler/RestTemplateResponseHandler.java +++ b/src/main/java/com/moabam/global/error/handler/RestTemplateResponseHandler.java @@ -1,6 +1,8 @@ package com.moabam.global.error.handler; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import org.springframework.http.HttpStatusCode; import org.springframework.http.client.ClientHttpResponse; @@ -14,7 +16,7 @@ public class RestTemplateResponseHandler implements ResponseErrorHandler { @Override - public boolean hasError(ClientHttpResponse response) { + public boolean hasError(ClientHttpResponse response) throws IOException { try { return response.getStatusCode().isError(); } catch (IOException ioException) { @@ -25,13 +27,29 @@ public boolean hasError(ClientHttpResponse response) { @Override public void handleError(ClientHttpResponse response) { try { + String errorMessage = parseErrorMessage(response); HttpStatusCode statusCode = response.getStatusCode(); + validResponse(statusCode); } catch (IOException ioException) { throw new BadRequestException(ErrorMessage.REQUEST_FAILED); } } + private String parseErrorMessage(ClientHttpResponse response) throws IOException { + BufferedReader errorMessage = new BufferedReader(new InputStreamReader(response.getBody())); + + String line = errorMessage.readLine(); + StringBuilder sb = new StringBuilder(); + + while (line != null) { + sb.append(line).append("\n"); + line = errorMessage.readLine(); + } + + return sb.toString(); + } + private void validResponse(HttpStatusCode statusCode) { if (statusCode.is5xxServerError()) { throw new BadRequestException(ErrorMessage.REQUEST_FAILED); diff --git a/src/main/java/com/moabam/global/error/model/ErrorMessage.java b/src/main/java/com/moabam/global/error/model/ErrorMessage.java index cd39ce68..571fca38 100644 --- a/src/main/java/com/moabam/global/error/model/ErrorMessage.java +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -34,6 +34,7 @@ public enum ErrorMessage { IMAGE_CONVERT_FAIL("이미지 변환을 실패했습니다."), LOGIN_FAILED("로그인에 실패했습니다."), + LOGIN_FAILED_ADMIN_KEY("어드민키가 달라요"), REQUEST_FAILED("네트워크 접근 실패입니다."), GRANT_FAILED("인가 코드 실패"), AUTHENTICATE_FAIL("인증 실패"), diff --git a/src/main/resources/config b/src/main/resources/config index 4dbde487..e1f5d131 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit 4dbde487226312c2a144165aa28c0cc0b1e7a0bf +Subproject commit e1f5d131da4190129655289e3700a276e3630e6b diff --git a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java index b4d2e47d..93f6c5a3 100644 --- a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java @@ -89,13 +89,13 @@ public void initParams() { oauthConfig = new OAuthConfig( new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", - "http://tokenInfo/url", "https://deleteRequest/url"), + "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", List.of("profile_nickname", "profile_image"), "adminKey")); ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); noOAuthConfig = new OAuthConfig( - new OAuthConfig.Provider(null, null, null, null, null), + new OAuthConfig.Provider(null, null, null, null, null, null), new OAuthConfig.Client(null, null, null, null, null, null)); noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, oAuth2AuthorizationServerRequestService, memberService, jwtProviderService, tokenRepository, cookieUtils); @@ -167,7 +167,8 @@ void authorization_grant_success() { @Test void token_request_mapping_failBy_code() { // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, null)) + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, + null, oauthConfig.provider().redirectUri())) .isInstanceOf(NullPointerException.class); } @@ -175,7 +176,8 @@ void token_request_mapping_failBy_code() { @Test void token_request_mapping_failBy_config() { // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test")) + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", + oauthConfig.provider().redirectUri())) .isInstanceOf(NullPointerException.class); } @@ -185,7 +187,7 @@ void token_request_mapping_success() { // Given String code = "Test"; AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oauthConfig, code); + oauthConfig, code, oauthConfig.provider().redirectUri()); // When + Then assertThat(authorizationTokenRequest).isNotNull(); diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index d246c5df..1dbb1843 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -38,6 +39,11 @@ public class RankingServiceTest { @Autowired RankingService rankingService; + @BeforeEach + void init() { + redisTemplate.delete("Ranking"); + } + @DisplayName("redis에 추가") @Nested class Add { diff --git a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java index d0df0151..46e2d9bf 100644 --- a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java @@ -89,7 +89,7 @@ void setUp() { RestTemplate restTemplate = restTemplateBuilder.build(); ReflectionTestUtils.setField(oAuth2AuthorizationServerRequestService, "restTemplate", restTemplate); mockRestServiceServer = MockRestServiceServer.createServer(restTemplate); - willReturn(true).given(corsFilter).secureMatch(any(), any()); + willReturn(true).given(corsFilter).secureMatch(any()); } @DisplayName("인가 코드 받기 위한 로그인 페이지 요청") diff --git a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java index 9e124cb2..f9b866d4 100644 --- a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -40,6 +41,11 @@ class RankingControllerTest extends WithoutFilterSupporter { @Autowired RedisTemplate redisTemplate; + @BeforeEach + void init() { + redisTemplate.delete("Ranking"); + } + @DisplayName("") @WithMember @Test diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index a381738f..0077057d 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -32,7 +32,7 @@ public class WithoutFilterSupporter { @BeforeEach void setUpMock() { willReturn(true) - .given(corsFilter).secureMatch(any(), any()); + .given(corsFilter).secureMatch(any()); willReturn(Optional.of(PathResolver.Path.builder() .uri("/") From 4771e9e7ffba34621ce433635dfb4a4dc6314af8 Mon Sep 17 00:00:00 2001 From: parksey Date: Fri, 1 Dec 2023 19:40:06 +0900 Subject: [PATCH 13/20] =?UTF-8?q?refactor:=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=20config=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/config b/src/main/resources/config index e1f5d131..b217c205 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit e1f5d131da4190129655289e3700a276e3630e6b +Subproject commit b217c205120a8b9f25277ec1819c7141bbb5591c From 16a7eabcd906e139315bfb831e67545d8f7db9c4 Mon Sep 17 00:00:00 2001 From: parksey Date: Fri, 1 Dec 2023 20:10:55 +0900 Subject: [PATCH 14/20] =?UTF-8?q?fix:=20test=20application.yml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 976ad494..c485b58d 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -62,6 +62,7 @@ oauth2: token-uri: https://kauth.kakao.com/oauth/token token-info: https://kapi.kakao.com/v1/user/access_token_info unlink: https://kapi.kakao.com/v1/user/unlink + admin-redirect-uri: https://dev-admin.moabam.com/login/kakao/oauth token: @@ -70,7 +71,12 @@ token: refresh-expire: 150000 secret-key: testestestestestestestestestesttestestestestestestestestestest -allow: "" +allows: + origin: + - "https://test.com" + - "https://test.com" + +admin: moamoamoabam # Payment payment: From fc0fcb00180111c24d7aeba3226b65520c0e6a28 Mon Sep 17 00:00:00 2001 From: parksey Date: Fri, 1 Dec 2023 21:38:38 +0900 Subject: [PATCH 15/20] =?UTF-8?q?test:=20stub=EC=97=90=EC=84=9C=EC=9D=98?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/presentation/MemberAuthorizeControllerTest.java | 2 +- .../com/moabam/support/common/WithoutFilterSupporter.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java index 46e2d9bf..05c8a83e 100644 --- a/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberAuthorizeControllerTest.java @@ -89,7 +89,7 @@ void setUp() { RestTemplate restTemplate = restTemplateBuilder.build(); ReflectionTestUtils.setField(oAuth2AuthorizationServerRequestService, "restTemplate", restTemplate); mockRestServiceServer = MockRestServiceServer.createServer(restTemplate); - willReturn(true).given(corsFilter).secureMatch(any()); + willReturn("http://localhost").given(corsFilter).secureMatch(any()); } @DisplayName("인가 코드 받기 위한 로그인 페이지 요청") diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index 0077057d..18d726cc 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -15,6 +15,7 @@ import com.moabam.api.domain.member.Role; import com.moabam.global.auth.filter.CorsFilter; import com.moabam.global.auth.handler.PathResolver; +import com.moabam.global.config.AllowOriginConfig; @Import(DataCleanResolver.class) @ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) @@ -29,9 +30,12 @@ public class WithoutFilterSupporter { @SpyBean private CorsFilter corsFilter; + @SpyBean + private AllowOriginConfig allowOriginConfig; + @BeforeEach void setUpMock() { - willReturn(true) + willReturn("http://localhost:8080") .given(corsFilter).secureMatch(any()); willReturn(Optional.of(PathResolver.Path.builder() From fd99886a886af183f697d06ce0bfb0ee664182d6 Mon Sep 17 00:00:00 2001 From: parksey Date: Fri, 1 Dec 2023 21:57:52 +0900 Subject: [PATCH 16/20] =?UTF-8?q?style:=20=EB=B3=80=EC=88=98=EB=A9=B4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/moabam/admin/domain/admin/AdminRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java b/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java index eb3b37f0..e4878786 100644 --- a/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java +++ b/src/main/java/com/moabam/admin/domain/admin/AdminRepository.java @@ -6,5 +6,5 @@ public interface AdminRepository extends JpaRepository { - Optional findBySocialId(String s); + Optional findBySocialId(String socialId); } From df92c4f393937bc1b075833f7b6b90bf975e3646 Mon Sep 17 00:00:00 2001 From: parksey Date: Sun, 3 Dec 2023 10:23:14 +0900 Subject: [PATCH 17/20] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=EA=B3=BC=20=EC=9D=BC=EB=B0=98=20=EC=9C=A0=EC=A0=80=EA=B0=84=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=83=9D=EC=84=B1,=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/application/admin/AdminService.java | 16 +- .../com/moabam/admin/domain/admin/Admin.java | 2 +- .../presentation/admin/AdminController.java | 8 +- .../auth/AuthorizationService.java | 394 ++- .../auth/JwtAuthenticationService.java | 70 +- .../application/auth/JwtProviderService.java | 24 +- .../application/ranking/RankingMapper.java | 55 +- .../application/ranking/RankingService.java | 96 +- .../room/CertificationService.java | 373 +- .../api/application/room/RoomService.java | 435 ++- .../auth/repository/TokenRepository.java | 50 +- .../auth/filter/AuthorizationFilter.java | 191 +- .../moabam/global/auth/filter/CorsFilter.java | 114 +- .../global/common/util/CookieUtils.java | 6 +- .../global/config/AllowOriginConfig.java | 8 +- .../com/moabam/global/config/TokenConfig.java | 6 +- .../global/error/model/ErrorMessage.java | 1 + .../auth/AuthorizationServiceTest.java | 673 ++-- .../auth/JwtAuthenticationServiceTest.java | 16 +- .../auth/JwtProviderServiceTest.java | 12 +- .../redis/TokenRepostiroyTest.java | 7 +- .../presentation/MemberControllerTest.java | 5 +- .../presentation/RankingControllerTest.java | 131 +- .../api/presentation/RoomControllerTest.java | 3079 ++++++++--------- .../global/common/util/CookieMakeTest.java | 8 +- .../filter/AuthorizationFilterTest.java | 11 +- .../support/common/WithFilterSupporter.java | 3 +- .../common/WithoutFilterSupporter.java | 53 +- .../support/fixture/JwtProviderFixture.java | 4 +- src/test/resources/application.yml | 3 + 30 files changed, 2947 insertions(+), 2907 deletions(-) diff --git a/src/main/java/com/moabam/admin/application/admin/AdminService.java b/src/main/java/com/moabam/admin/application/admin/AdminService.java index 892f15cf..fa46782b 100644 --- a/src/main/java/com/moabam/admin/application/admin/AdminService.java +++ b/src/main/java/com/moabam/admin/application/admin/AdminService.java @@ -7,14 +7,13 @@ import com.moabam.admin.domain.admin.Admin; import com.moabam.admin.domain.admin.AdminRepository; -import com.moabam.api.application.auth.AuthorizationService; import com.moabam.api.application.auth.mapper.AuthMapper; import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; import com.moabam.api.dto.auth.LoginResponse; import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.exception.NotFoundException; import com.moabam.global.error.model.ErrorMessage; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @Service @@ -24,7 +23,6 @@ public class AdminService { @Value("${admin}") private String adminLoginKey; - private final AuthorizationService authorizationService; private final AdminRepository adminRepository; public void validate(String state) { @@ -33,12 +31,8 @@ public void validate(String state) { } } - public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, - AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { - LoginResponse loginResponse = login(authorizationTokenInfoResponse); - authorizationService.issueServiceToken(httpServletResponse, loginResponse.publicClaim()); - - return loginResponse; + public LoginResponse signUpOrLogin(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + return login(authorizationTokenInfoResponse); } private LoginResponse login(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { @@ -53,4 +47,8 @@ private Admin signUp(Long socialId) { return adminRepository.save(admin); } + + public Admin findMember(Long id) { + return adminRepository.findById(id).orElseThrow(() -> new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)); + } } diff --git a/src/main/java/com/moabam/admin/domain/admin/Admin.java b/src/main/java/com/moabam/admin/domain/admin/Admin.java index d63cbeff..eeff1bc3 100644 --- a/src/main/java/com/moabam/admin/domain/admin/Admin.java +++ b/src/main/java/com/moabam/admin/domain/admin/Admin.java @@ -37,7 +37,7 @@ public class Admin extends BaseTimeEntity { @Enumerated(EnumType.STRING) @Column(name = "role", nullable = false) - @ColumnDefault("'USER'") + @ColumnDefault("'ADMIN'") private Role role; @Builder diff --git a/src/main/java/com/moabam/admin/presentation/admin/AdminController.java b/src/main/java/com/moabam/admin/presentation/admin/AdminController.java index 8ef4354b..cea5bb99 100644 --- a/src/main/java/com/moabam/admin/presentation/admin/AdminController.java +++ b/src/main/java/com/moabam/admin/presentation/admin/AdminController.java @@ -31,9 +31,11 @@ public LoginResponse authorizationTokenIssue(@RequestBody AuthorizationCodeRespo HttpServletResponse httpServletResponse) { adminService.validate(authorizationCodeResponse.state()); AuthorizationTokenResponse tokenResponse = authorizationService.requestAdminToken(authorizationCodeResponse); - AuthorizationTokenInfoResponse authorizationTokenInfoResponse = authorizationService.requestTokenInfo( - tokenResponse); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = + authorizationService.requestTokenInfo(tokenResponse); + LoginResponse loginResponse = adminService.signUpOrLogin(authorizationTokenInfoResponse); + authorizationService.issueServiceToken(httpServletResponse, loginResponse.publicClaim()); - return adminService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + return loginResponse; } } diff --git a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java index a2db2e9f..4e040d07 100644 --- a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java +++ b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java @@ -1,216 +1,232 @@ package com.moabam.api.application.auth; -import java.util.Arrays; - -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponentsBuilder; - +import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthMapper; import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.application.member.MemberService; import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; -import com.moabam.api.dto.auth.AuthorizationCodeRequest; -import com.moabam.api.dto.auth.AuthorizationCodeResponse; -import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; -import com.moabam.api.dto.auth.AuthorizationTokenRequest; -import com.moabam.api.dto.auth.AuthorizationTokenResponse; -import com.moabam.api.dto.auth.LoginResponse; -import com.moabam.api.dto.auth.TokenSaveValue; +import com.moabam.api.domain.member.Role; +import com.moabam.api.dto.auth.*; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; +import com.moabam.global.common.util.CookieUtils; import com.moabam.global.common.util.GlobalConstant; -import com.moabam.global.common.util.cookie.CookieUtils; +import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.config.OAuthConfig; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; - import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Arrays; @Slf4j @Service @RequiredArgsConstructor public class AuthorizationService { - private final FcmService fcmService; - private final OAuthConfig oAuthConfig; - private final TokenConfig tokenConfig; - private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; - private final MemberService memberService; - private final JwtProviderService jwtProviderService; - private final TokenRepository tokenRepository; - private final CookieUtils cookieUtils; - - public void redirectToLoginPage(HttpServletResponse httpServletResponse) { - String authorizationCodeUri = getAuthorizationCodeUri(); - oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); - } - - public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), - oAuthConfig.provider().adminRedirectUri()); - } - - public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); - } - - public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { - String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); - ResponseEntity authorizationTokenInfoResponse = - oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); - - return authorizationTokenInfoResponse.getBody(); - } - - public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, - AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { - LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); - issueServiceToken(httpServletResponse, loginResponse.publicClaim()); - - return loginResponse; - } - - public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { - String accessToken = jwtProviderService.provideAccessToken(publicClaim); - String refreshToken = jwtProviderService.provideRefreshToken(); - TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); - - tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest); - - response.addCookie( - cookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire())); - response.addCookie( - cookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire())); - response.addCookie( - cookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire())); - } - - public void validTokenPair(Long id, String oldRefreshToken) { - TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id); - - if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { - tokenRepository.delete(id); - - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, - HttpServletResponse httpServletResponse) { - removeToken(httpServletRequest, httpServletResponse); - tokenRepository.delete(authMember.id()); - fcmService.deleteTokenByMemberId(authMember.id()); - } - - public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - if (httpServletRequest.getCookies() == null) { - return; - } - - Arrays.stream(httpServletRequest.getCookies()) - .forEach(cookie -> { - if (cookie.getName().contains("token")) { - httpServletResponse.addCookie(cookieUtils.deleteCookie(cookie)); - } - }); - } - - @Transactional - public void unLinkMember(AuthMember authMember) { - Member member = memberService.findMemberToDelete(authMember.id()); - unlinkRequest(member.getSocialId()); - memberService.delete(member); - } - - private void unlinkRequest(String socialId) { - try { - oauth2AuthorizationServerRequestService.unlinkMemberRequest( - oAuthConfig.provider().unlink(), - oAuthConfig.client().adminKey(), - unlinkRequestParam(socialId)); - log.info("회원 탈퇴 성공 : [socialId={}]", socialId); - } catch (BadRequestException badRequestException) { - log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); - throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); - } - } - - private MultiValueMap unlinkRequestParam(String socialId) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("target_id_type", "user_id"); - params.add("target_id", socialId); - - return params; - } - - private String getAuthorizationCodeUri() { - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); - return generateQueryParamsWith(authorizationCodeRequest); - } - - private String generateTokenValue(String token) { - return "Bearer" + GlobalConstant.SPACE + token; - } - - private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { - UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( - oAuthConfig.provider().authorizationUri()) - .queryParam("response_type", "code") - .queryParam("client_id", authorizationCodeRequest.clientId()) - .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); - - if (authorizationCodeRequest.scope() != null - && !authorizationCodeRequest.scope().isEmpty()) { - String scopes = String.join(",", authorizationCodeRequest.scope()); - authorizationCodeUri.queryParam("scope", scopes); - } - - return authorizationCodeUri.toUriString(); - } - - private void validAuthorizationGrant(String code) { - if (code == null) { - throw new BadRequestException(ErrorMessage.GRANT_FAILED); - } - } - - private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oAuthConfig, code, redirectUri); - MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); - ResponseEntity authorizationTokenResponse = - oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(), - uriParams); - - return authorizationTokenResponse.getBody(); - } - - private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { - MultiValueMap contents = new LinkedMultiValueMap<>(); - contents.add("grant_type", authorizationTokenRequest.grantType()); - contents.add("client_id", authorizationTokenRequest.clientId()); - contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); - contents.add("code", authorizationTokenRequest.code()); - - if (authorizationTokenRequest.clientSecret() != null) { - contents.add("client_secret", authorizationTokenRequest.clientSecret()); - } - - return contents; - } + private final FcmService fcmService; + private final OAuthConfig oAuthConfig; + private final TokenConfig tokenConfig; + private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; + private final MemberService memberService; + private final AdminService adminService; + private final JwtProviderService jwtProviderService; + private final TokenRepository tokenRepository; + private final AllowOriginConfig allowOriginsConfig; + + public void redirectToLoginPage(HttpServletResponse httpServletResponse) { + String authorizationCodeUri = getAuthorizationCodeUri(); + oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); + } + + public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), + oAuthConfig.provider().adminRedirectUri()); + } + + public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); + } + + public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { + String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); + ResponseEntity authorizationTokenInfoResponse = + oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); + + return authorizationTokenInfoResponse.getBody(); + } + + public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, + AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); + issueServiceToken(httpServletResponse, loginResponse.publicClaim()); + + return loginResponse; + } + + public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { + String accessToken = jwtProviderService.provideAccessToken(publicClaim); + String refreshToken = jwtProviderService.provideRefreshToken(publicClaim.role()); + TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); + + tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest, publicClaim.role()); + + String domain = getDomain(publicClaim.role()); + + response.addCookie( + CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire(), domain)); + } + + public void validTokenPair(Long id, String oldRefreshToken, Role role) { + TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id, role); + + if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { + tokenRepository.delete(id, role); + + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + removeToken(httpServletRequest, httpServletResponse); + tokenRepository.delete(authMember.id(), authMember.role()); + fcmService.deleteTokenByMemberId(authMember.id()); + } + + public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + if (httpServletRequest.getCookies() == null) { + return; + } + + Arrays.stream(httpServletRequest.getCookies()) + .forEach(cookie -> { + if (cookie.getName().contains("token")) { + httpServletResponse.addCookie(CookieUtils.deleteCookie(cookie)); + } + }); + } + + @Transactional + public void unLinkMember(AuthMember authMember) { + Member member = memberService.findMemberToDelete(authMember.id()); + unlinkRequest(member.getSocialId()); + memberService.delete(member); + } + + private String getDomain(Role role) { + if (role.equals(Role.ADMIN)) { + return allowOriginsConfig.adminDomain(); + } + + return allowOriginsConfig.domain(); + } + + private void unlinkRequest(String socialId) { + try { + oauth2AuthorizationServerRequestService.unlinkMemberRequest( + oAuthConfig.provider().unlink(), + oAuthConfig.client().adminKey(), + unlinkRequestParam(socialId)); + log.info("회원 탈퇴 성공 : [socialId={}]", socialId); + } catch (BadRequestException badRequestException) { + log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); + throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); + } + } + + private MultiValueMap unlinkRequestParam(String socialId) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("target_id_type", "user_id"); + params.add("target_id", socialId); + + return params; + } + + private String getAuthorizationCodeUri() { + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); + return generateQueryParamsWith(authorizationCodeRequest); + } + + private String generateTokenValue(String token) { + return "Bearer" + GlobalConstant.SPACE + token; + } + + private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { + UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( + oAuthConfig.provider().authorizationUri()) + .queryParam("response_type", "code") + .queryParam("client_id", authorizationCodeRequest.clientId()) + .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); + + if (authorizationCodeRequest.scope() != null + && !authorizationCodeRequest.scope().isEmpty()) { + String scopes = String.join(",", authorizationCodeRequest.scope()); + authorizationCodeUri.queryParam("scope", scopes); + } + + return authorizationCodeUri.toUriString(); + } + + private void validAuthorizationGrant(String code) { + if (code == null) { + throw new BadRequestException(ErrorMessage.GRANT_FAILED); + } + } + + private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oAuthConfig, code, redirectUri); + MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); + ResponseEntity authorizationTokenResponse = + oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(), + uriParams); + + return authorizationTokenResponse.getBody(); + } + + private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { + MultiValueMap contents = new LinkedMultiValueMap<>(); + contents.add("grant_type", authorizationTokenRequest.grantType()); + contents.add("client_id", authorizationTokenRequest.clientId()); + contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); + contents.add("code", authorizationTokenRequest.code()); + + if (authorizationTokenRequest.clientSecret() != null) { + contents.add("client_secret", authorizationTokenRequest.clientSecret()); + } + + return contents; + } + + public void validMemberExist(Long id, Role role) { + if (role.equals(Role.ADMIN)) { + adminService.findMember(id); + + return; + } + + memberService.findMember(id); + } } diff --git a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java index eaf1d456..a2b75462 100644 --- a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java +++ b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java @@ -1,47 +1,57 @@ package com.moabam.api.application.auth; -import java.nio.charset.StandardCharsets; - -import org.json.JSONObject; -import org.springframework.stereotype.Service; - import com.moabam.api.application.auth.mapper.AuthorizationMapper; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; - import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.io.Decoders; import lombok.RequiredArgsConstructor; +import org.json.JSONObject; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.security.Key; @Service @RequiredArgsConstructor public class JwtAuthenticationService { - private final TokenConfig tokenConfig; - - public boolean isTokenExpire(String token) { - try { - Jwts.parserBuilder() - .setSigningKey(tokenConfig.getKey()) - .build() - .parseClaimsJws(token); - return false; - } catch (ExpiredJwtException expiredJwtException) { - return true; - } catch (Exception exception) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - public PublicClaim parseClaim(String token) { - String claims = token.split("\\.")[1]; - byte[] claimsBytes = Decoders.BASE64URL.decode(claims); - String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); - JSONObject jsonObject = new JSONObject(decodedClaims); - - return AuthorizationMapper.toPublicClaim(jsonObject); - } + private final TokenConfig tokenConfig; + + public boolean isTokenExpire(String token, Role role) { + try { + Key key = getSecret(role); + + Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return false; + } catch (ExpiredJwtException expiredJwtException) { + return true; + } catch (Exception exception) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + private Key getSecret(Role role) { + if (role.equals(Role.ADMIN)) { + return tokenConfig.getAdminKey(); + } + + return tokenConfig.getKey(); + } + + public PublicClaim parseClaim(String token) { + String claims = token.split("\\.")[1]; + byte[] claimsBytes = Decoders.BASE64URL.decode(claims); + String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); + JSONObject jsonObject = new JSONObject(decodedClaims); + + return AuthorizationMapper.toPublicClaim(jsonObject); + } } diff --git a/src/main/java/com/moabam/api/application/auth/JwtProviderService.java b/src/main/java/com/moabam/api/application/auth/JwtProviderService.java index 4ea924dd..816985f2 100644 --- a/src/main/java/com/moabam/api/application/auth/JwtProviderService.java +++ b/src/main/java/com/moabam/api/application/auth/JwtProviderService.java @@ -1,9 +1,11 @@ package com.moabam.api.application.auth; +import java.security.Key; import java.util.Date; import org.springframework.stereotype.Service; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; @@ -22,23 +24,23 @@ public String provideAccessToken(PublicClaim publicClaim) { return generateIdToken(publicClaim, tokenConfig.getAccessExpire()); } - public String provideRefreshToken() { - return generateCommonInfo(tokenConfig.getRefreshExpire()); + public String provideRefreshToken(Role role) { + return generateCommonInfo(tokenConfig.getRefreshExpire(), role); } private String generateIdToken(PublicClaim publicClaim, long expireTime) { - return commonInfo(expireTime) + return commonInfo(expireTime, publicClaim.role()) .claim("id", publicClaim.id()) .claim("nickname", publicClaim.nickname()) .claim("role", publicClaim.role()) .compact(); } - private String generateCommonInfo(long expireTime) { - return commonInfo(expireTime).compact(); + private String generateCommonInfo(long expireTime, Role role) { + return commonInfo(expireTime, role).compact(); } - private JwtBuilder commonInfo(long expireTime) { + private JwtBuilder commonInfo(long expireTime, Role role) { Date issueDate = new Date(); Date expireDate = new Date(issueDate.getTime() + expireTime); @@ -48,6 +50,14 @@ private JwtBuilder commonInfo(long expireTime) { .setIssuer(tokenConfig.getIss()) .setIssuedAt(issueDate) .setExpiration(expireDate) - .signWith(tokenConfig.getKey(), SignatureAlgorithm.HS256); + .signWith(getSecretKey(role), SignatureAlgorithm.HS256); + } + + private Key getSecretKey(Role role) { + if (role.equals(Role.ADMIN)) { + return tokenConfig.getAdminKey(); + } + + return tokenConfig.getKey(); } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index 06ad3389..5246815f 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -1,43 +1,42 @@ package com.moabam.api.application.ranking; -import java.util.List; - import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; - import lombok.AccessLevel; import lombok.NoArgsConstructor; +import java.util.List; + @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RankingMapper { - public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { - return TopRankingInfo.builder() - .rank(rank) - .score(score) - .nickname(rankInfo.nickname()) - .image(rankInfo.image()) - .memberId(rankInfo.memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { + return TopRankingInfo.builder() + .rank(rank) + .score(score) + .nickname(rankInfo.nickname()) + .image(rankInfo.image()) + .memberId(rankInfo.memberId()) + .build(); + } - public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { - return TopRankingInfo.builder() - .rank(rank) - .score(updateRanking.score()) - .nickname(updateRanking.rankingInfo().nickname()) - .image(updateRanking.rankingInfo().image()) - .memberId(updateRanking.rankingInfo().memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { + return TopRankingInfo.builder() + .rank(rank + 1) + .score(updateRanking.score()) + .nickname(updateRanking.rankingInfo().nickname()) + .image(updateRanking.rankingInfo().image()) + .memberId(updateRanking.rankingInfo().memberId()) + .build(); + } - public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, - List topRankings) { - return TopRankingResponse.builder() - .topRankings(topRankings) - .myRanking(myRanking) - .build(); - } + public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, + List topRankings) { + return TopRankingResponse.builder() + .topRankings(topRankings) + .myRanking(myRanking) + .build(); + } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index aa48370d..77dd7956 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -1,76 +1,74 @@ package com.moabam.api.application.ranking; -import static java.util.Objects.*; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.stereotype.Service; - import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; - import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static java.util.Objects.requireNonNull; @Service @RequiredArgsConstructor public class RankingService { - private static final String RANKING = "Ranking"; - private static final int START_INDEX = 0; - private static final int LIMIT_INDEX = 9; + private static final String RANKING = "Ranking"; + private static final int START_INDEX = 0; + private static final int LIMIT_INDEX = 9; - private final ObjectMapper objectMapper; - private final ZSetRedisRepository zSetRedisRepository; + private final ObjectMapper objectMapper; + private final ZSetRedisRepository zSetRedisRepository; - public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { - zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); - } + public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { + zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); + } - public void updateScores(List updateRankings) { - updateRankings.forEach(updateRanking -> - zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); - } + public void updateScores(List updateRankings) { + updateRankings.forEach(updateRanking -> + zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); + } - public void changeInfos(RankingInfo before, RankingInfo after) { - zSetRedisRepository.changeMember(RANKING, before, after); - } + public void changeInfos(RankingInfo before, RankingInfo after) { + zSetRedisRepository.changeMember(RANKING, before, after); + } - public void removeRanking(RankingInfo rankingInfo) { - zSetRedisRepository.delete(RANKING, rankingInfo); - } + public void removeRanking(RankingInfo rankingInfo) { + zSetRedisRepository.delete(RANKING, rankingInfo); + } - public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { - List topRankings = getTopRankings(); - Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); - TopRankingInfo myRankingInfoResponse = - RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); + public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { + List topRankings = getTopRankings(); + Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); + TopRankingInfo myRankingInfoResponse = + RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); - return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); - } + return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); + } - private List getTopRankings() { - Set> topRankings = - zSetRedisRepository.rangeJson(RANKING, START_INDEX, LIMIT_INDEX); + private List getTopRankings() { + Set> topRankings = + zSetRedisRepository.rangeJson(RANKING, START_INDEX, LIMIT_INDEX); - Set scoreSet = new HashSet<>(); - List topRankingInfo = new ArrayList<>(); + Set scoreSet = new HashSet<>(); + List topRankingInfo = new ArrayList<>(); - for (ZSetOperations.TypedTuple topRanking : topRankings) { - long score = requireNonNull(topRanking.getScore()).longValue(); - scoreSet.add(score); + for (ZSetOperations.TypedTuple topRanking : topRankings) { + long score = requireNonNull(topRanking.getScore()).longValue(); + scoreSet.add(score); - RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); - topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); - } + RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); + topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + } - return topRankingInfo; - } + return topRankingInfo; + } } diff --git a/src/main/java/com/moabam/api/application/room/CertificationService.java b/src/main/java/com/moabam/api/application/room/CertificationService.java index 6f024e6f..82345f61 100644 --- a/src/main/java/com/moabam/api/application/room/CertificationService.java +++ b/src/main/java/com/moabam/api/application/room/CertificationService.java @@ -1,213 +1,200 @@ package com.moabam.api.application.room; -import static com.moabam.global.common.util.GlobalConstant.*; -import static com.moabam.global.error.model.ErrorMessage.*; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.moabam.api.application.bug.BugService; import com.moabam.api.application.member.MemberService; import com.moabam.api.application.room.mapper.CertificationsMapper; import com.moabam.api.domain.bug.BugType; import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.room.Certification; -import com.moabam.api.domain.room.DailyMemberCertification; -import com.moabam.api.domain.room.DailyRoomCertification; -import com.moabam.api.domain.room.Participant; -import com.moabam.api.domain.room.Room; -import com.moabam.api.domain.room.RoomExp; -import com.moabam.api.domain.room.Routine; -import com.moabam.api.domain.room.repository.CertificationRepository; -import com.moabam.api.domain.room.repository.CertificationsSearchRepository; -import com.moabam.api.domain.room.repository.DailyMemberCertificationRepository; -import com.moabam.api.domain.room.repository.DailyRoomCertificationRepository; -import com.moabam.api.domain.room.repository.ParticipantSearchRepository; -import com.moabam.api.domain.room.repository.RoutineRepository; +import com.moabam.api.domain.room.*; +import com.moabam.api.domain.room.repository.*; import com.moabam.api.dto.room.CertifiedMemberInfo; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.common.util.UrlSubstringParser; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.NotFoundException; - import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static com.moabam.global.common.util.GlobalConstant.MIDNIGHT_HOUR; +import static com.moabam.global.error.model.ErrorMessage.*; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class CertificationService { - private static final int REQUIRED_ROOM_CERTIFICATION = 75; - - private final RoutineRepository routineRepository; - private final CertificationRepository certificationRepository; - private final ParticipantSearchRepository participantSearchRepository; - private final CertificationsSearchRepository certificationsSearchRepository; - private final DailyRoomCertificationRepository dailyRoomCertificationRepository; - private final DailyMemberCertificationRepository dailyMemberCertificationRepository; - private final MemberService memberService; - private final BugService bugService; - private final ClockHolder clockHolder; - - @Transactional - public CertifiedMemberInfo getCertifiedMemberInfo(Long memberId, Long roomId, List imageUrls) { - LocalDate today = clockHolder.date(); - Participant participant = participantSearchRepository.findOne(memberId, roomId) - .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); - Room room = participant.getRoom(); - Member member = memberService.findMember(memberId); - BugType bugType = switch (room.getRoomType()) { - case MORNING -> BugType.MORNING; - case NIGHT -> BugType.NIGHT; - }; - - validateCertifyTime(clockHolder.times(), room.getCertifyTime()); - validateAlreadyCertified(memberId, roomId, today); - - certifyMember(memberId, roomId, participant, member, imageUrls); - - return CertificationsMapper.toCertifiedMemberInfo(today, bugType, room, member); - } - - @Transactional - public void certifyRoom(CertifiedMemberInfo certifyInfo) { - LocalDate date = certifyInfo.date(); - BugType bugType = certifyInfo.bugType(); - Room room = certifyInfo.room(); - Member member = certifyInfo.member(); - - Optional dailyRoomCertification = - certificationsSearchRepository.findDailyRoomCertification(room.getId(), date); - - if (dailyRoomCertification.isEmpty()) { - certifyRoomIfAvailable(room.getId(), date, room, bugType, room.getLevel()); - return; - } - - bugService.reward(member, bugType, room.getLevel()); - } - - public boolean existsMemberCertification(Long memberId, Long roomId, LocalDate date) { - return dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(memberId, roomId, - date.atStartOfDay(), date.atTime(LocalTime.MAX)); - } - - public boolean existsRoomCertification(Long roomId, LocalDate date) { - return dailyRoomCertificationRepository.existsByRoomIdAndCertifiedAt(roomId, date); - } - - public boolean existsAnyMemberCertification(Long roomId, LocalDate date) { - return dailyMemberCertificationRepository.existsByRoomIdAndCreatedAtBetween(roomId, date.atStartOfDay(), - date.atTime(LocalTime.MAX)); - } - - public Certification findCertification(Long certificationId) { - return certificationRepository.findById(certificationId) - .orElseThrow(() -> new NotFoundException(CERTIFICATION_NOT_FOUND)); - } - - public List findCertifications(List routines) { - return certificationsSearchRepository.findCertificationsByRoutines(routines); - } - - public void deleteCertifications(List certifications) { - certificationRepository.deleteAll(certifications); - } - - private void validateCertifyTime(LocalDateTime now, int certifyTime) { - LocalTime targetTime = LocalTime.of(certifyTime, 0); - LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); - - if (certifyTime == MIDNIGHT_HOUR && now.getHour() != MIDNIGHT_HOUR) { - targetDateTime = targetDateTime.plusDays(1); - } - - LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); - - if (now.isBefore(targetDateTime) || now.isAfter(plusTenMinutes)) { - throw new BadRequestException(INVALID_CERTIFY_TIME); - } - } - - private void validateAlreadyCertified(Long memberId, Long roomId, LocalDate today) { - if (certificationsSearchRepository.findDailyMemberCertification(memberId, roomId, today).isPresent()) { - throw new BadRequestException(DUPLICATED_DAILY_MEMBER_CERTIFICATION); - } - } - - private void certifyMember(Long memberId, Long roomId, Participant participant, Member member, List urls) { - DailyMemberCertification dailyMemberCertification = CertificationsMapper.toDailyMemberCertification(memberId, - roomId, participant); - dailyMemberCertificationRepository.save(dailyMemberCertification); - member.increaseTotalCertifyCount(); - participant.updateCertifyCount(); - - saveNewCertifications(memberId, urls); - } - - private void saveNewCertifications(Long memberId, List imageUrls) { - List certifications = new ArrayList<>(); - - for (String imageUrl : imageUrls) { - Long routineId = Long.parseLong(UrlSubstringParser.parseUrl(imageUrl, "_")); - Routine routine = routineRepository.findById(routineId).orElseThrow(() -> new NotFoundException( - ROUTINE_NOT_FOUND)); - - Certification certification = CertificationsMapper.toCertification(routine, memberId, imageUrl); - certifications.add(certification); - } - - certificationRepository.saveAll(certifications); - } - - private void certifyRoomIfAvailable(Long roomId, LocalDate today, Room room, BugType bugType, int roomLevel) { - List dailyMemberCertifications = - certificationsSearchRepository.findSortedDailyMemberCertifications(roomId, today); - double completePercentage = calculateCompletePercentage(dailyMemberCertifications.size(), - room.getCurrentUserCount()); - - if (completePercentage >= REQUIRED_ROOM_CERTIFICATION) { - DailyRoomCertification createDailyRoomCertification = CertificationsMapper.toDailyRoomCertification( - roomId, today); - - dailyRoomCertificationRepository.save(createDailyRoomCertification); - int expAppliedRoomLevel = getRoomLevelAfterExpApply(roomLevel, room); - - provideBugToCompletedMembers(bugType, dailyMemberCertifications, expAppliedRoomLevel); - } - } - - private double calculateCompletePercentage(int certifiedMembersCount, int currentsMembersCount) { - double completePercentage = ((double)certifiedMembersCount / currentsMembersCount) * 100; - - return Math.round(completePercentage * 100) / 100.0; - } - - private int getRoomLevelAfterExpApply(int roomLevel, Room room) { - int requireExp = RoomExp.of(roomLevel).getTotalExp(); - room.gainExp(); - - if (room.getExp() == requireExp) { - room.levelUp(); - } - - return room.getLevel(); - } - - private void provideBugToCompletedMembers(BugType bugType, List dailyMemberCertifications, - int expAppliedRoomLevel) { - List memberIds = dailyMemberCertifications.stream() - .map(DailyMemberCertification::getMemberId) - .toList(); - - memberService.getRoomMembers(memberIds) - .forEach(completedMember -> bugService.reward(completedMember, bugType, expAppliedRoomLevel)); - } + private static final int REQUIRED_ROOM_CERTIFICATION = 75; + + private final RoutineRepository routineRepository; + private final CertificationRepository certificationRepository; + private final ParticipantSearchRepository participantSearchRepository; + private final CertificationsSearchRepository certificationsSearchRepository; + private final DailyRoomCertificationRepository dailyRoomCertificationRepository; + private final DailyMemberCertificationRepository dailyMemberCertificationRepository; + private final MemberService memberService; + private final BugService bugService; + private final ClockHolder clockHolder; + + @Transactional + public CertifiedMemberInfo getCertifiedMemberInfo(Long memberId, Long roomId, List imageUrls) { + LocalDate today = clockHolder.date(); + Participant participant = participantSearchRepository.findOne(memberId, roomId) + .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); + Room room = participant.getRoom(); + Member member = memberService.findMember(memberId); + BugType bugType = switch (room.getRoomType()) { + case MORNING -> BugType.MORNING; + case NIGHT -> BugType.NIGHT; + }; + + validateCertifyTime(clockHolder.times(), room.getCertifyTime()); + validateAlreadyCertified(memberId, roomId, today); + + certifyMember(memberId, roomId, participant, member, imageUrls); + + return CertificationsMapper.toCertifiedMemberInfo(today, bugType, room, member); + } + + @Transactional + public void certifyRoom(CertifiedMemberInfo certifyInfo) { + LocalDate date = certifyInfo.date(); + BugType bugType = certifyInfo.bugType(); + Room room = certifyInfo.room(); + Member member = certifyInfo.member(); + + Optional dailyRoomCertification = + certificationsSearchRepository.findDailyRoomCertification(room.getId(), date); + + if (dailyRoomCertification.isEmpty()) { + certifyRoomIfAvailable(room.getId(), date, room, bugType, room.getLevel()); + return; + } + + bugService.reward(member, bugType, room.getLevel()); + } + + public boolean existsMemberCertification(Long memberId, Long roomId, LocalDate date) { + return dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(memberId, roomId, + date.atStartOfDay(), date.atTime(LocalTime.MAX)); + } + + public boolean existsRoomCertification(Long roomId, LocalDate date) { + return dailyRoomCertificationRepository.existsByRoomIdAndCertifiedAt(roomId, date); + } + + public boolean existsAnyMemberCertification(Long roomId, LocalDate date) { + return dailyMemberCertificationRepository.existsByRoomIdAndCreatedAtBetween(roomId, date.atStartOfDay(), + date.atTime(LocalTime.MAX)); + } + + public Certification findCertification(Long certificationId) { + return certificationRepository.findById(certificationId) + .orElseThrow(() -> new NotFoundException(CERTIFICATION_NOT_FOUND)); + } + + public List findCertifications(List routines) { + return certificationsSearchRepository.findCertificationsByRoutines(routines); + } + + public void deleteCertifications(List certifications) { + certificationRepository.deleteAll(certifications); + } + + private void validateCertifyTime(LocalDateTime now, int certifyTime) { + LocalTime targetTime = LocalTime.of(certifyTime, 0); + LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); + + if (certifyTime == MIDNIGHT_HOUR && now.getHour() != MIDNIGHT_HOUR) { + targetDateTime = targetDateTime.plusDays(1); + } + + LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); + + if (now.isBefore(targetDateTime) || now.isAfter(plusTenMinutes)) { + throw new BadRequestException(INVALID_CERTIFY_TIME); + } + } + + private void validateAlreadyCertified(Long memberId, Long roomId, LocalDate today) { + if (certificationsSearchRepository.findDailyMemberCertification(memberId, roomId, today).isPresent()) { + throw new BadRequestException(DUPLICATED_DAILY_MEMBER_CERTIFICATION); + } + } + + private void certifyMember(Long memberId, Long roomId, Participant participant, Member member, List urls) { + DailyMemberCertification dailyMemberCertification = CertificationsMapper.toDailyMemberCertification(memberId, + roomId, participant); + dailyMemberCertificationRepository.save(dailyMemberCertification); + member.increaseTotalCertifyCount(); + participant.updateCertifyCount(); + + saveNewCertifications(memberId, urls); + } + + private void saveNewCertifications(Long memberId, List imageUrls) { + List certifications = new ArrayList<>(); + + for (String imageUrl : imageUrls) { + Long routineId = Long.parseLong(UrlSubstringParser.parseUrl(imageUrl, "_")); + Routine routine = routineRepository.findById(routineId).orElseThrow(() -> new NotFoundException( + ROUTINE_NOT_FOUND)); + + Certification certification = CertificationsMapper.toCertification(routine, memberId, imageUrl); + certifications.add(certification); + } + + certificationRepository.saveAll(certifications); + } + + private void certifyRoomIfAvailable(Long roomId, LocalDate today, Room room, BugType bugType, int roomLevel) { + List dailyMemberCertifications = + certificationsSearchRepository.findSortedDailyMemberCertifications(roomId, today); + double completePercentage = calculateCompletePercentage(dailyMemberCertifications.size(), + room.getCurrentUserCount()); + + if (completePercentage >= REQUIRED_ROOM_CERTIFICATION) { + DailyRoomCertification createDailyRoomCertification = CertificationsMapper.toDailyRoomCertification( + roomId, today); + + dailyRoomCertificationRepository.save(createDailyRoomCertification); + int expAppliedRoomLevel = getRoomLevelAfterExpApply(roomLevel, room); + + provideBugToCompletedMembers(bugType, dailyMemberCertifications, expAppliedRoomLevel); + } + } + + private double calculateCompletePercentage(int certifiedMembersCount, int currentsMembersCount) { + double completePercentage = ((double) certifiedMembersCount / currentsMembersCount) * 100; + + return Math.round(completePercentage * 100) / 100.0; + } + + private int getRoomLevelAfterExpApply(int roomLevel, Room room) { + int requireExp = RoomExp.of(roomLevel).getTotalExp(); + room.gainExp(); + + if (room.getExp() == requireExp) { + room.levelUp(); + } + + return room.getLevel(); + } + + private void provideBugToCompletedMembers(BugType bugType, List dailyMemberCertifications, + int expAppliedRoomLevel) { + List memberIds = dailyMemberCertifications.stream() + .map(DailyMemberCertification::getMemberId) + .toList(); + + memberService.getRoomMembers(memberIds) + .forEach(completedMember -> bugService.reward(completedMember, bugType, expAppliedRoomLevel)); + } } diff --git a/src/main/java/com/moabam/api/application/room/RoomService.java b/src/main/java/com/moabam/api/application/room/RoomService.java index 48dd9d04..2a8fae16 100644 --- a/src/main/java/com/moabam/api/application/room/RoomService.java +++ b/src/main/java/com/moabam/api/application/room/RoomService.java @@ -1,31 +1,12 @@ package com.moabam.api.application.room; -import static com.moabam.api.domain.room.RoomType.*; -import static com.moabam.global.error.model.ErrorMessage.*; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.moabam.api.application.member.MemberService; import com.moabam.api.application.room.mapper.ParticipantMapper; import com.moabam.api.application.room.mapper.RoomMapper; import com.moabam.api.application.room.mapper.RoutineMapper; import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.room.Certification; -import com.moabam.api.domain.room.Participant; -import com.moabam.api.domain.room.Room; -import com.moabam.api.domain.room.RoomType; -import com.moabam.api.domain.room.Routine; -import com.moabam.api.domain.room.repository.DailyMemberCertificationRepository; -import com.moabam.api.domain.room.repository.ParticipantRepository; -import com.moabam.api.domain.room.repository.ParticipantSearchRepository; -import com.moabam.api.domain.room.repository.RoomRepository; -import com.moabam.api.domain.room.repository.RoutineRepository; +import com.moabam.api.domain.room.*; +import com.moabam.api.domain.room.repository.*; import com.moabam.api.dto.room.CreateRoomRequest; import com.moabam.api.dto.room.EnterRoomRequest; import com.moabam.api.dto.room.ModifyRoomRequest; @@ -33,9 +14,19 @@ import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.ForbiddenException; import com.moabam.global.error.exception.NotFoundException; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; + +import static com.moabam.api.domain.room.RoomType.MORNING; +import static com.moabam.api.domain.room.RoomType.NIGHT; +import static com.moabam.global.error.model.ErrorMessage.*; @Service @RequiredArgsConstructor @@ -43,204 +34,204 @@ @Transactional(readOnly = true) public class RoomService { - private final RoomRepository roomRepository; - private final RoutineRepository routineRepository; - private final ParticipantRepository participantRepository; - private final ParticipantSearchRepository participantSearchRepository; - private final DailyMemberCertificationRepository dailyMemberCertificationRepository; - private final CertificationService certificationService; - private final MemberService memberService; - private final ClockHolder clockHolder; - - @Transactional - public Long createRoom(Long memberId, CreateRoomRequest createRoomRequest) { - Room room = RoomMapper.toRoomEntity(createRoomRequest); - List routines = RoutineMapper.toRoutineEntities(room, createRoomRequest.routines()); - Participant participant = ParticipantMapper.toParticipant(room, memberId); - - validateEnteredRoomCount(memberId, room.getRoomType()); - - Member member = memberService.findMember(memberId); - member.enterRoom(room.getRoomType()); - participant.enableManager(); - room.changeManagerNickname(member.getNickname()); - - Room savedRoom = roomRepository.save(room); - routineRepository.saveAll(routines); - participantRepository.save(participant); - - return savedRoom.getId(); - } - - @Transactional - public void modifyRoom(Long memberId, Long roomId, ModifyRoomRequest modifyRoomRequest) { - Participant participant = getParticipant(memberId, roomId); - validateManagerAuthorization(participant); - - Room room = participant.getRoom(); - room.changeTitle(modifyRoomRequest.title()); - room.changeAnnouncement(modifyRoomRequest.announcement()); - room.changePassword(modifyRoomRequest.password()); - room.changeMaxCount(modifyRoomRequest.maxUserCount()); - - if (room.getCertifyTime() != modifyRoomRequest.certifyTime()) { - validateChangeCertifyTime(roomId); - } - room.changeCertifyTime(modifyRoomRequest.certifyTime()); - } - - @Transactional - public void enterRoom(Long memberId, Long roomId, EnterRoomRequest enterRoomRequest) { - Room room = roomRepository.findWithPessimisticLockById(roomId).orElseThrow( - () -> new NotFoundException(ROOM_NOT_FOUND)); - validateRoomEnter(memberId, enterRoomRequest.password(), room); - - Member member = memberService.findMember(memberId); - member.enterRoom(room.getRoomType()); - room.increaseCurrentUserCount(); - - Participant participant = ParticipantMapper.toParticipant(room, memberId); - participantRepository.save(participant); - } - - @Transactional - public void exitRoom(Long memberId, Long roomId) { - Participant participant = getParticipant(memberId, roomId); - Room room = participant.getRoom(); - - validateRoomExit(participant, room); - - Member member = memberService.findMember(memberId); - member.exitRoom(room.getRoomType()); - - participant.removeRoom(); - participantRepository.flush(); - participantRepository.delete(participant); - - if (!participant.isManager()) { - room.decreaseCurrentUserCount(); - return; - } - - List routines = routineRepository.findAllByRoomId(roomId); - List certifications = certificationService.findCertifications(routines); - - certificationService.deleteCertifications(certifications); - routineRepository.deleteAll(routines); - roomRepository.delete(room); - } - - @Transactional - public void mandateManager(Long managerId, Long roomId, Long memberId) { - Participant managerParticipant = getParticipant(managerId, roomId); - Participant memberParticipant = getParticipant(memberId, roomId); - validateManagerAuthorization(managerParticipant); - - Room room = managerParticipant.getRoom(); - Member member = memberService.findMember(memberParticipant.getMemberId()); - room.changeManagerNickname(member.getNickname()); - - managerParticipant.disableManager(); - memberParticipant.enableManager(); - } - - @Transactional - public void deportParticipant(Long managerId, Long roomId, Long memberId) { - validateDeportParticipant(managerId, memberId); - Participant managerParticipant = getParticipant(managerId, roomId); - Participant memberParticipant = getParticipant(memberId, roomId); - validateManagerAuthorization(managerParticipant); - - Room room = managerParticipant.getRoom(); - memberParticipant.removeRoom(); - participantRepository.flush(); - participantRepository.delete(memberParticipant); - room.decreaseCurrentUserCount(); - - Member member = memberService.findMember(memberId); - member.exitRoom(room.getRoomType()); - } - - public boolean checkIfParticipant(Long memberId, Long roomId) { - try { - getParticipant(memberId, roomId); - return true; - } catch (NotFoundException e) { - return false; - } - } - - public Room findRoom(Long roomId) { - return roomRepository.findById(roomId) - .orElseThrow(() -> new NotFoundException(ROOM_NOT_FOUND)); - } - - private void validateChangeCertifyTime(Long roomId) { - if (certificationService.existsAnyMemberCertification(roomId, clockHolder.date())) { - throw new BadRequestException(UNAVAILABLE_TO_CHANGE_CERTIFY_TIME); - } - } - - private Participant getParticipant(Long memberId, Long roomId) { - return participantSearchRepository.findOne(memberId, roomId) - .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); - } - - private void validateDeportParticipant(Long managerId, Long memberId) { - if (managerId.equals(memberId)) { - throw new BadRequestException(PARTICIPANT_DEPORT_ERROR); - } - } - - private void validateManagerAuthorization(Participant participant) { - if (!participant.isManager()) { - throw new ForbiddenException(ROOM_MODIFY_UNAUTHORIZED_REQUEST); - } - } - - private void validateRoomEnter(Long memberId, String requestPassword, Room room) { - validateEnteredRoomCount(memberId, room.getRoomType()); - validateCertifyTime(room); - - if (!StringUtils.isEmpty(requestPassword) && !room.getPassword().equals(requestPassword)) { - throw new BadRequestException(WRONG_ROOM_PASSWORD); - } - if (room.getCurrentUserCount() == room.getMaxUserCount()) { - throw new BadRequestException(ROOM_MAX_USER_REACHED); - } - } - - private void validateEnteredRoomCount(Long memberId, RoomType roomType) { - Member member = memberService.findMember(memberId); - - if (roomType.equals(MORNING) && member.getCurrentMorningCount() >= 3) { - throw new BadRequestException(MEMBER_ROOM_EXCEED); - } - if (roomType.equals(NIGHT) && member.getCurrentNightCount() >= 3) { - throw new BadRequestException(MEMBER_ROOM_EXCEED); - } - } - - private void validateCertifyTime(Room room) { - LocalDateTime now = clockHolder.times(); - LocalTime targetTime = LocalTime.of(room.getCertifyTime(), 0); - LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); - - LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); - - if (now.isAfter(targetDateTime) && now.isBefore(plusTenMinutes)) { - throw new BadRequestException(ROOM_ENTER_FAILED); - } - } - - private void validateRoomExit(Participant participant, Room room) { - if (participant.isManager() && room.getCurrentUserCount() != 1) { - throw new BadRequestException(ROOM_EXIT_MANAGER_FAIL); - } - - if (dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(participant.getMemberId(), - room.getId(), clockHolder.date().atStartOfDay(), clockHolder.date().atTime(LocalTime.MAX))) { - throw new BadRequestException(CERTIFIED_ROOM_EXIT_FAILED); - } - } + private final RoomRepository roomRepository; + private final RoutineRepository routineRepository; + private final ParticipantRepository participantRepository; + private final ParticipantSearchRepository participantSearchRepository; + private final DailyMemberCertificationRepository dailyMemberCertificationRepository; + private final CertificationService certificationService; + private final MemberService memberService; + private final ClockHolder clockHolder; + + @Transactional + public Long createRoom(Long memberId, CreateRoomRequest createRoomRequest) { + Room room = RoomMapper.toRoomEntity(createRoomRequest); + List routines = RoutineMapper.toRoutineEntities(room, createRoomRequest.routines()); + Participant participant = ParticipantMapper.toParticipant(room, memberId); + + validateEnteredRoomCount(memberId, room.getRoomType()); + + Member member = memberService.findMember(memberId); + member.enterRoom(room.getRoomType()); + participant.enableManager(); + room.changeManagerNickname(member.getNickname()); + + Room savedRoom = roomRepository.save(room); + routineRepository.saveAll(routines); + participantRepository.save(participant); + + return savedRoom.getId(); + } + + @Transactional + public void modifyRoom(Long memberId, Long roomId, ModifyRoomRequest modifyRoomRequest) { + Participant participant = getParticipant(memberId, roomId); + validateManagerAuthorization(participant); + + Room room = participant.getRoom(); + room.changeTitle(modifyRoomRequest.title()); + room.changeAnnouncement(modifyRoomRequest.announcement()); + room.changePassword(modifyRoomRequest.password()); + room.changeMaxCount(modifyRoomRequest.maxUserCount()); + + if (room.getCertifyTime() != modifyRoomRequest.certifyTime()) { + validateChangeCertifyTime(roomId); + } + room.changeCertifyTime(modifyRoomRequest.certifyTime()); + } + + @Transactional + public void enterRoom(Long memberId, Long roomId, EnterRoomRequest enterRoomRequest) { + Room room = roomRepository.findWithPessimisticLockById(roomId).orElseThrow( + () -> new NotFoundException(ROOM_NOT_FOUND)); + validateRoomEnter(memberId, enterRoomRequest.password(), room); + + Member member = memberService.findMember(memberId); + member.enterRoom(room.getRoomType()); + room.increaseCurrentUserCount(); + + Participant participant = ParticipantMapper.toParticipant(room, memberId); + participantRepository.save(participant); + } + + @Transactional + public void exitRoom(Long memberId, Long roomId) { + Participant participant = getParticipant(memberId, roomId); + Room room = participant.getRoom(); + + validateRoomExit(participant, room); + + Member member = memberService.findMember(memberId); + member.exitRoom(room.getRoomType()); + + participant.removeRoom(); + participantRepository.flush(); + participantRepository.delete(participant); + + if (!participant.isManager()) { + room.decreaseCurrentUserCount(); + return; + } + + List routines = routineRepository.findAllByRoomId(roomId); + List certifications = certificationService.findCertifications(routines); + + certificationService.deleteCertifications(certifications); + routineRepository.deleteAll(routines); + roomRepository.delete(room); + } + + @Transactional + public void mandateManager(Long managerId, Long roomId, Long memberId) { + Participant managerParticipant = getParticipant(managerId, roomId); + Participant memberParticipant = getParticipant(memberId, roomId); + validateManagerAuthorization(managerParticipant); + + Room room = managerParticipant.getRoom(); + Member member = memberService.findMember(memberParticipant.getMemberId()); + room.changeManagerNickname(member.getNickname()); + + managerParticipant.disableManager(); + memberParticipant.enableManager(); + } + + @Transactional + public void deportParticipant(Long managerId, Long roomId, Long memberId) { + validateDeportParticipant(managerId, memberId); + Participant managerParticipant = getParticipant(managerId, roomId); + Participant memberParticipant = getParticipant(memberId, roomId); + validateManagerAuthorization(managerParticipant); + + Room room = managerParticipant.getRoom(); + memberParticipant.removeRoom(); + participantRepository.flush(); + participantRepository.delete(memberParticipant); + room.decreaseCurrentUserCount(); + + Member member = memberService.findMember(memberId); + member.exitRoom(room.getRoomType()); + } + + public boolean checkIfParticipant(Long memberId, Long roomId) { + try { + getParticipant(memberId, roomId); + return true; + } catch (NotFoundException e) { + return false; + } + } + + public Room findRoom(Long roomId) { + return roomRepository.findById(roomId) + .orElseThrow(() -> new NotFoundException(ROOM_NOT_FOUND)); + } + + private void validateChangeCertifyTime(Long roomId) { + if (certificationService.existsAnyMemberCertification(roomId, clockHolder.date())) { + throw new BadRequestException(UNAVAILABLE_TO_CHANGE_CERTIFY_TIME); + } + } + + private Participant getParticipant(Long memberId, Long roomId) { + return participantSearchRepository.findOne(memberId, roomId) + .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); + } + + private void validateDeportParticipant(Long managerId, Long memberId) { + if (managerId.equals(memberId)) { + throw new BadRequestException(PARTICIPANT_DEPORT_ERROR); + } + } + + private void validateManagerAuthorization(Participant participant) { + if (!participant.isManager()) { + throw new ForbiddenException(ROOM_MODIFY_UNAUTHORIZED_REQUEST); + } + } + + private void validateRoomEnter(Long memberId, String requestPassword, Room room) { + validateEnteredRoomCount(memberId, room.getRoomType()); + validateCertifyTime(room); + + if (!StringUtils.isEmpty(requestPassword) && !room.getPassword().equals(requestPassword)) { + throw new BadRequestException(WRONG_ROOM_PASSWORD); + } + if (room.getCurrentUserCount() == room.getMaxUserCount()) { + throw new BadRequestException(ROOM_MAX_USER_REACHED); + } + } + + private void validateEnteredRoomCount(Long memberId, RoomType roomType) { + Member member = memberService.findMember(memberId); + + if (roomType.equals(MORNING) && member.getCurrentMorningCount() >= 3) { + throw new BadRequestException(MEMBER_ROOM_EXCEED); + } + if (roomType.equals(NIGHT) && member.getCurrentNightCount() >= 3) { + throw new BadRequestException(MEMBER_ROOM_EXCEED); + } + } + + private void validateCertifyTime(Room room) { + LocalDateTime now = clockHolder.times(); + LocalTime targetTime = LocalTime.of(room.getCertifyTime(), 0); + LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); + + LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); + + if (now.isAfter(targetDateTime) && now.isBefore(plusTenMinutes)) { + throw new BadRequestException(ROOM_ENTER_FAILED); + } + } + + private void validateRoomExit(Participant participant, Room room) { + if (participant.isManager() && room.getCurrentUserCount() != 1) { + throw new BadRequestException(ROOM_EXIT_MANAGER_FAIL); + } + + if (dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(participant.getMemberId(), + room.getId(), clockHolder.date().atStartOfDay(), clockHolder.date().atTime(LocalTime.MAX))) { + throw new BadRequestException(CERTIFIED_ROOM_EXIT_FAILED); + } + } } diff --git a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java index 3286d0e4..0e5fba58 100644 --- a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java +++ b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java @@ -1,42 +1,42 @@ package com.moabam.api.domain.auth.repository; -import java.time.Duration; - +import com.moabam.api.domain.member.Role; +import com.moabam.api.dto.auth.TokenSaveValue; +import com.moabam.api.infrastructure.redis.HashRedisRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; -import com.moabam.api.dto.auth.TokenSaveValue; -import com.moabam.api.infrastructure.redis.HashRedisRepository; +import java.time.Duration; @Repository public class TokenRepository { - private static final int EXPIRE_DAYS = 14; + private static final int EXPIRE_DAYS = 14; - private final HashRedisRepository hashRedisRepository; + private final HashRedisRepository hashRedisRepository; - @Autowired - public TokenRepository(HashRedisRepository hashRedisRepository) { - this.hashRedisRepository = hashRedisRepository; - } + @Autowired + public TokenRepository(HashRedisRepository hashRedisRepository) { + this.hashRedisRepository = hashRedisRepository; + } - public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest) { - String tokenKey = parseTokenKey(memberId); + public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest, Role role) { + String tokenKey = parseTokenKey(memberId, role); - hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); - } + hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); + } - public TokenSaveValue getTokenSaveValue(Long memberId) { - String tokenKey = parseTokenKey(memberId); - return (TokenSaveValue)hashRedisRepository.get(tokenKey); - } + public TokenSaveValue getTokenSaveValue(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + return (TokenSaveValue) hashRedisRepository.get(tokenKey); + } - public void delete(Long memberId) { - String tokenKey = parseTokenKey(memberId); - hashRedisRepository.delete(tokenKey); - } + public void delete(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + hashRedisRepository.delete(tokenKey); + } - private String parseTokenKey(Long memberId) { - return "auth_" + memberId; - } + private String parseTokenKey(Long memberId, Role role) { + return role.name() + "_" + memberId; + } } diff --git a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java index 325afe37..ded3a74a 100644 --- a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java @@ -1,23 +1,14 @@ package com.moabam.global.auth.filter; -import java.io.IOException; -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; - -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - import com.moabam.api.application.auth.AuthorizationService; import com.moabam.api.application.auth.JwtAuthenticationService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.AuthorizationThreadLocal; import com.moabam.global.auth.model.PublicClaim; +import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; - import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -26,6 +17,15 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; @Slf4j @Order(2) @@ -33,82 +33,95 @@ @RequiredArgsConstructor public class AuthorizationFilter extends OncePerRequestFilter { - private final HandlerExceptionResolver handlerExceptionResolver; - private final JwtAuthenticationService authenticationService; - private final AuthorizationService authorizationService; - - @Override - protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, - @NotNull HttpServletResponse httpServletResponse, - @NotNull FilterChain filterChain) throws ServletException, IOException { - - if (isPermit(httpServletRequest)) { - filterChain.doFilter(httpServletRequest, httpServletResponse); - return; - } - - try { - invoke(httpServletRequest, httpServletResponse); - } catch (UnauthorizedException unauthorizedException) { - authorizationService.removeToken(httpServletRequest, httpServletResponse); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - private boolean isPermit(HttpServletRequest httpServletRequest) { - Boolean isPermit = (Boolean)httpServletRequest.getAttribute("isPermit"); - - return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); - } - - private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - Cookie[] cookies = getCookiesOrThrow(httpServletRequest); - - if (!isTokenTypeBearer(cookies)) { - throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); - } - - handleTokenAuthenticate(cookies, httpServletResponse); - } - - private boolean isTokenTypeBearer(Cookie[] cookies) { - return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); - } - - private void handleTokenAuthenticate(Cookie[] cookies, - HttpServletResponse httpServletResponse) { - String accessToken = extractTokenFromCookie(cookies, "access_token"); - PublicClaim publicClaim = authenticationService.parseClaim(accessToken); - - if (authenticationService.isTokenExpire(accessToken)) { - String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); - - if (authenticationService.isTokenExpire(refreshToken)) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - - authorizationService.validTokenPair(publicClaim.id(), refreshToken); - authorizationService.issueServiceToken(httpServletResponse, publicClaim); - } - - AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); - } - - private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { - return Optional.ofNullable(httpServletRequest.getCookies()) - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); - } - - private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { - return Arrays.stream(cookies) - .filter(cookie -> tokenName.equals(cookie.getName())) - .map(Cookie::getValue) - .findFirst() - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); - } + private final HandlerExceptionResolver handlerExceptionResolver; + private final JwtAuthenticationService authenticationService; + private final AuthorizationService authorizationService; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, + @NotNull HttpServletResponse httpServletResponse, + @NotNull FilterChain filterChain) throws ServletException, IOException { + + if (isPermit(httpServletRequest)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + + try { + invoke(httpServletRequest, httpServletResponse); + } catch (UnauthorizedException unauthorizedException) { + authorizationService.removeToken(httpServletRequest, httpServletResponse); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + private boolean isPermit(HttpServletRequest httpServletRequest) { + Boolean isPermit = (Boolean) httpServletRequest.getAttribute("isPermit"); + + return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); + } + + private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + Cookie[] cookies = getCookiesOrThrow(httpServletRequest); + + if (!isTokenTypeBearer(cookies)) { + throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); + } + + handleTokenAuthenticate(cookies, httpServletResponse, httpServletRequest); + } + + private boolean isTokenTypeBearer(Cookie[] cookies) { + return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); + } + + private void handleTokenAuthenticate(Cookie[] cookies, + HttpServletResponse httpServletResponse, HttpServletRequest httpServletRequest) { + String accessToken = extractTokenFromCookie(cookies, "access_token"); + PublicClaim publicClaim = authenticationService.parseClaim(accessToken); + + if (authenticationService.isTokenExpire(accessToken, publicClaim.role())) { + String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); + + if (authenticationService.isTokenExpire(refreshToken, publicClaim.role())) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + + validInvalidMember(publicClaim, refreshToken, httpServletRequest); + authorizationService.issueServiceToken(httpServletResponse, publicClaim); + } + + AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); + } + + private void validInvalidMember(PublicClaim publicClaim, String refreshToken, + HttpServletRequest httpServletRequest) { + boolean isAdminPath = httpServletRequest.getRequestURI().contains("admins"); + + if (!((publicClaim.role().equals(Role.ADMIN) && isAdminPath) + || (publicClaim.role().equals(Role.USER) && !isAdminPath))) { + throw new BadRequestException(ErrorMessage.INVALID_REQUEST_ROLE); + } + + authorizationService.validTokenPair(publicClaim.id(), refreshToken, publicClaim.role()); + authorizationService.validMemberExist(publicClaim.id(), publicClaim.role()); + } + + private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { + return Optional.ofNullable(httpServletRequest.getCookies()) + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); + } + + private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { + return Arrays.stream(cookies) + .filter(cookie -> tokenName.equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); + } } diff --git a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java index f363c2df..aaafe1b5 100644 --- a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java @@ -1,24 +1,22 @@ package com.moabam.global.auth.filter; -import java.io.IOException; -import java.util.Objects; - -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - import com.google.cloud.storage.HttpMethod; import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; - import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; +import java.util.Objects; @Slf4j @Order(0) @@ -26,53 +24,53 @@ @RequiredArgsConstructor public class CorsFilter extends OncePerRequestFilter { - private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; - private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " - + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; - - private final HandlerExceptionResolver handlerExceptionResolver; - - private final AllowOriginConfig allowOriginsConfig; - - @Override - protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, - FilterChain filterChain) throws ServletException, IOException { - String refer = httpServletRequest.getHeader("referer"); - String origin = secureMatch(refer); - - try { - if (Objects.isNull(origin)) { - throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); - } - } catch (UnauthorizedException unauthorizedException) { - log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); - httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); - httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); - httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); - httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); - - if (isOption(httpServletRequest.getMethod())) { - httpServletRequest.setAttribute("isPermit", true); - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - public String secureMatch(String refer) { - return allowOriginsConfig.origin().stream() - .filter(refer::contains) - .findFirst() - .orElse(null); - } - - public boolean isOption(String method) { - return HttpMethod.OPTIONS.name().equals(method); - } + private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; + private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " + + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; + + private final HandlerExceptionResolver handlerExceptionResolver; + + private final AllowOriginConfig allowOriginsConfig; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + String refer = httpServletRequest.getHeader("referer"); + String origin = secureMatch(refer); + + try { + if (Objects.isNull(origin)) { + throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); + } + } catch (UnauthorizedException unauthorizedException) { + log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); + httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); + httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); + + if (isOption(httpServletRequest.getMethod())) { + httpServletRequest.setAttribute("isPermit", true); + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + public String secureMatch(String refer) { + return allowOriginsConfig.origin().stream() + .filter(refer::contains) + .findFirst() + .orElse(null); + } + + public boolean isOption(String method) { + return HttpMethod.OPTIONS.name().equals(method); + } } diff --git a/src/main/java/com/moabam/global/common/util/CookieUtils.java b/src/main/java/com/moabam/global/common/util/CookieUtils.java index 892268da..5f59441d 100644 --- a/src/main/java/com/moabam/global/common/util/CookieUtils.java +++ b/src/main/java/com/moabam/global/common/util/CookieUtils.java @@ -7,10 +7,11 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CookieUtils { - public static Cookie tokenCookie(String name, String value, long expireTime) { + public static Cookie tokenCookie(String name, String value, long expireTime, String domain) { Cookie cookie = new Cookie(name, value); cookie.setSecure(true); cookie.setHttpOnly(true); + cookie.setDomain(domain); cookie.setPath("/"); cookie.setMaxAge((int)expireTime); cookie.setAttribute("SameSite", "Lax"); @@ -18,10 +19,11 @@ public static Cookie tokenCookie(String name, String value, long expireTime) { return cookie; } - public static Cookie typeCookie(String value, long expireTime) { + public static Cookie typeCookie(String value, long expireTime, String domain) { Cookie cookie = new Cookie("token_type", value); cookie.setSecure(true); cookie.setHttpOnly(true); + cookie.setDomain(domain); cookie.setPath("/"); cookie.setMaxAge((int)expireTime); cookie.setAttribute("SameSite", "Lax"); diff --git a/src/main/java/com/moabam/global/config/AllowOriginConfig.java b/src/main/java/com/moabam/global/config/AllowOriginConfig.java index d2ae8db6..3392dd90 100644 --- a/src/main/java/com/moabam/global/config/AllowOriginConfig.java +++ b/src/main/java/com/moabam/global/config/AllowOriginConfig.java @@ -1,12 +1,14 @@ package com.moabam.global.config; -import java.util.List; - import org.springframework.boot.context.properties.ConfigurationProperties; +import java.util.List; + @ConfigurationProperties(prefix = "allows") public record AllowOriginConfig( - List origin + String adminDomain, + String domain, + List origin ) { } diff --git a/src/main/java/com/moabam/global/config/TokenConfig.java b/src/main/java/com/moabam/global/config/TokenConfig.java index fe6bdc91..4bbe5a1e 100644 --- a/src/main/java/com/moabam/global/config/TokenConfig.java +++ b/src/main/java/com/moabam/global/config/TokenConfig.java @@ -16,13 +16,17 @@ public class TokenConfig { private final long accessExpire; private final long refreshExpire; private final String secretKey; + private final String adminSecret; private final Key key; + private final Key adminKey; - public TokenConfig(String iss, long accessExpire, long refreshExpire, String secretKey) { + public TokenConfig(String iss, long accessExpire, long refreshExpire, String secretKey, String adminSecret) { this.iss = iss; this.accessExpire = accessExpire; this.refreshExpire = refreshExpire; this.secretKey = secretKey; + this.adminSecret = adminSecret; this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); + this.adminKey = Keys.hmacShaKeyFor(adminSecret.getBytes(StandardCharsets.UTF_8)); } } diff --git a/src/main/java/com/moabam/global/error/model/ErrorMessage.java b/src/main/java/com/moabam/global/error/model/ErrorMessage.java index 3684e3ec..ee5584c7 100644 --- a/src/main/java/com/moabam/global/error/model/ErrorMessage.java +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -12,6 +12,7 @@ public enum ErrorMessage { INVALID_REQUEST_VALUE_TYPE_FORMAT("'%s' 값은 유효한 %s 값이 아닙니다."), NOT_FOUND_AVAILABLE_PORT("사용 가능한 포트를 찾을 수 없습니다. (10000 ~ 65535)"), ERROR_EXECUTING_EMBEDDED_REDIS("Embedded Redis 실행 중 오류가 발생했습니다."), + INVALID_REQUEST_ROLE("회원은 회원에, 어드민은 어드민에 연결해야 합니다."), REPORT_REQUEST_ERROR("신고 요청하고자 하는 방이나 대상이 존재하지 않습니다."), diff --git a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java index 93f6c5a3..678428ff 100644 --- a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java @@ -1,43 +1,17 @@ package com.moabam.api.application.auth; -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; - -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; - +import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.application.member.MemberService; import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; -import com.moabam.api.dto.auth.AuthorizationCodeRequest; -import com.moabam.api.dto.auth.AuthorizationCodeResponse; -import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; -import com.moabam.api.dto.auth.AuthorizationTokenRequest; -import com.moabam.api.dto.auth.AuthorizationTokenResponse; -import com.moabam.api.dto.auth.LoginResponse; +import com.moabam.api.domain.member.Role; +import com.moabam.api.dto.auth.*; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; -import com.moabam.global.common.util.cookie.CookieDevUtils; -import com.moabam.global.common.util.cookie.CookieUtils; +import com.moabam.global.common.util.CookieUtils; +import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.config.OAuthConfig; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.BadRequestException; @@ -49,311 +23,344 @@ import com.moabam.support.fixture.AuthorizationResponseFixture; import com.moabam.support.fixture.MemberFixture; import com.moabam.support.fixture.TokenSaveValueFixture; - import jakarta.servlet.http.Cookie; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; @ExtendWith({MockitoExtension.class, FilterProcessExtension.class}) class AuthorizationServiceTest { - @InjectMocks - AuthorizationService authorizationService; - - @Mock - OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; - - @Mock - MemberService memberService; - - @Mock - JwtProviderService jwtProviderService; - - @Mock - FcmService fcmService; - - @Mock - TokenRepository tokenRepository; - - CookieUtils cookieUtils; - OAuthConfig oauthConfig; - TokenConfig tokenConfig; - AuthorizationService noPropertyService; - OAuthConfig noOAuthConfig; - - @BeforeEach - public void initParams() { - cookieUtils = new CookieDevUtils(); - tokenConfig = new TokenConfig(null, 100000, 150000, - "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"); - ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); - ReflectionTestUtils.setField(authorizationService, "cookieUtils", cookieUtils); - - oauthConfig = new OAuthConfig( - new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", - "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), - new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", - List.of("profile_nickname", "profile_image"), "adminKey")); - ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); - - noOAuthConfig = new OAuthConfig( - new OAuthConfig.Provider(null, null, null, null, null, null), - new OAuthConfig.Client(null, null, null, null, null, null)); - noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, - oAuth2AuthorizationServerRequestService, memberService, jwtProviderService, tokenRepository, cookieUtils); - } - - @DisplayName("인가코드 URI 생성 매퍼 실패") - @Test - void authorization_code_request_mapping_fail() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("인가코드 URI 생성 매퍼 성공") - @Test - void authorization_code_request_mapping_success() { - // Given - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); - - // When + Then - assertThat(authorizationCodeRequest).isNotNull(); - assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); - } - - @DisplayName("redirect 로그인페이지 성공") - @Test - void redirect_loginPage_success() { - // given - MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); - - // when - authorizationService.redirectToLoginPage(mockHttpServletResponse); - - // then - verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); - } - - @DisplayName("인가코드 반환 실패") - @Test - void authorization_grant_fail() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", - "errorDescription", null); - - // When + Then - assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( - BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); - } - - @DisplayName("인가코드 반환 성공") - @Test - void authorization_grant_success() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = - new AuthorizationCodeResponse("test", null, null, null); - AuthorizationTokenResponse authorizationTokenResponse = - AuthorizationResponseFixture.authorizationTokenResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( - new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); - } - - @DisplayName("토큰 요청 매퍼 실패 - code null") - @Test - void token_request_mapping_failBy_code() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, - null, oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 실패 - config 에러") - @Test - void token_request_mapping_failBy_config() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", - oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 성공") - @Test - void token_request_mapping_success() { - // Given - String code = "Test"; - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oauthConfig, code, oauthConfig.provider().redirectUri()); - - // When + Then - assertThat(authorizationTokenRequest).isNotNull(); - assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), - () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); - } - - @DisplayName("토큰 변경 성공") - @Test - void generate_token() { - // Given - AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); - AuthorizationTokenInfoResponse tokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), - eq("Bearer " + tokenResponse.accessToken()))).thenReturn( - new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); - - // Then - assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); - } - - @DisplayName("회원 가입 및 로그인 성공 테스트") - @ParameterizedTest - @ValueSource(booleans = {true, false}) - void signUp_success(boolean isSignUp) { - // given - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - AuthorizationTokenInfoResponse authorizationTokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - LoginResponse loginResponse = LoginResponse.builder() - .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").build()) - .isSignUp(isSignUp) - .build(); - - willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); - - // when - LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); - - // then - assertThat(loginResponse).isEqualTo(result); - - Cookie tokenType = httpServletResponse.getCookie("token_type"); - assertThat(tokenType).isNotNull(); - assertThat(tokenType.getValue()).isEqualTo("Bearer"); - - Cookie accessCookie = httpServletResponse.getCookie("access_token"); - assertThat(accessCookie).isNotNull(); - assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), - () -> assertThat(accessCookie.isHttpOnly()).isTrue(), - () -> assertThat(accessCookie.getPath()).isEqualTo("/")); - - Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); - assertThat(refreshCookie).isNotNull(); - assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), - () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), - () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); - } - - @DisplayName("토큰 redis 검증") - @Test - void valid_token_in_redis() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")).given(tokenRepository).getTokenSaveValue(1L); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.validTokenPair(1L, "token")); - } - - @DisplayName("이전 토큰과 동일한지 검증") - @Test - void valid_token_failby_notEquals_token() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")).given(tokenRepository).getTokenSaveValue(1L); - - // When + Then - assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken")).isInstanceOf( - UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); - verify(tokenRepository).delete(1L); - } - - @DisplayName("토큰 삭제 성공") - @Test - void error_with_expire_token(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - httpServletRequest.setCookies(cookieUtils.tokenCookie("access_token", "value", 100000), - cookieUtils.tokenCookie("refresh_token", "value", 100000), cookieUtils.typeCookie("Bearer", 100000)); - - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - Cookie cookie = httpServletResponse.getCookie("access_token"); - - // Then - assertThat(cookie).isNotNull(); - assertThat(cookie.getMaxAge()).isZero(); - assertThat(cookie.getValue()).isEqualTo("value"); - - verify(tokenRepository).delete(authMember.id()); - } - - @DisplayName("토큰 없어서 삭제 실패") - @Test - void token_null_delete_fail(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - - // Then - assertThat(httpServletResponse.getCookies()).isEmpty(); - } - - @DisplayName("회원 탈퇴 요청 성공") - @Test - void unlink_success(@WithMember AuthMember authMember) { - // given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - doNothing().when(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); - } - - @DisplayName("회원이 없어서 찾기 실패") - @Test - void unlink_failBy_find_Member(@WithMember AuthMember authMember) { - // Given + When - willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) - .given(memberService) - .findMemberToDelete(authMember.id()); - - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(NotFoundException.class) - .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); - } - - @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") - @Test - void unlink_failBy_(@WithMember AuthMember authMember) { - // Given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - willThrow(BadRequestException.class) - .given(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(BadRequestException.class) - .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); - } + @InjectMocks + AuthorizationService authorizationService; + + @Mock + OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; + + @Mock + MemberService memberService; + + @Mock + AdminService adminService; + + @Mock + JwtProviderService jwtProviderService; + + @Mock + FcmService fcmService; + + @Mock + TokenRepository tokenRepository; + + AllowOriginConfig allowOriginsConfig; + OAuthConfig oauthConfig; + TokenConfig tokenConfig; + AuthorizationService noPropertyService; + OAuthConfig noOAuthConfig; + String domain = "Test"; + + @BeforeEach + public void initParams() { + String secretKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + String adminKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + + allowOriginsConfig = new AllowOriginConfig(domain, domain, List.of("test", "test")); + ReflectionTestUtils.setField(authorizationService, "allowOriginsConfig", allowOriginsConfig); + tokenConfig = new TokenConfig(null, 100000, 150000, secretKey, adminKey); + ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); + + oauthConfig = new OAuthConfig( + new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", + "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), + new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", + List.of("profile_nickname", "profile_image"), "adminKey")); + ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); + + noOAuthConfig = new OAuthConfig( + new OAuthConfig.Provider(null, null, null, null, null, null), + new OAuthConfig.Client(null, null, null, null, null, null)); + noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, + oAuth2AuthorizationServerRequestService, memberService, adminService, + jwtProviderService, tokenRepository, allowOriginsConfig); + } + + @DisplayName("인가코드 URI 생성 매퍼 실패") + @Test + void authorization_code_request_mapping_fail() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("인가코드 URI 생성 매퍼 성공") + @Test + void authorization_code_request_mapping_success() { + // Given + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); + + // When + Then + assertThat(authorizationCodeRequest).isNotNull(); + assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); + } + + @DisplayName("redirect 로그인페이지 성공") + @Test + void redirect_loginPage_success() { + // given + MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); + + // when + authorizationService.redirectToLoginPage(mockHttpServletResponse); + + // then + verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); + } + + @DisplayName("인가코드 반환 실패") + @Test + void authorization_grant_fail() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", + "errorDescription", null); + + // When + Then + assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( + BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); + } + + @DisplayName("인가코드 반환 성공") + @Test + void authorization_grant_success() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = + new AuthorizationCodeResponse("test", null, null, null); + AuthorizationTokenResponse authorizationTokenResponse = + AuthorizationResponseFixture.authorizationTokenResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( + new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); + } + + @DisplayName("토큰 요청 매퍼 실패 - code null") + @Test + void token_request_mapping_failBy_code() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, + null, oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 실패 - config 에러") + @Test + void token_request_mapping_failBy_config() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", + oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 성공") + @Test + void token_request_mapping_success() { + // Given + String code = "Test"; + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oauthConfig, code, oauthConfig.provider().redirectUri()); + + // When + Then + assertThat(authorizationTokenRequest).isNotNull(); + assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), + () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); + } + + @DisplayName("토큰 변경 성공") + @Test + void generate_token() { + // Given + AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); + AuthorizationTokenInfoResponse tokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), + eq("Bearer " + tokenResponse.accessToken()))).thenReturn( + new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); + + // Then + assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); + } + + @DisplayName("회원 가입 및 로그인 성공 테스트") + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void signUp_success(boolean isSignUp) { + // given + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + LoginResponse loginResponse = LoginResponse.builder() + .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").role(Role.USER).build()) + .isSignUp(isSignUp) + .build(); + + willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); + + // when + LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + + // then + assertThat(loginResponse).isEqualTo(result); + + Cookie tokenType = httpServletResponse.getCookie("token_type"); + assertThat(tokenType).isNotNull(); + assertThat(tokenType.getValue()).isEqualTo("Bearer"); + + Cookie accessCookie = httpServletResponse.getCookie("access_token"); + assertThat(accessCookie).isNotNull(); + assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), + () -> assertThat(accessCookie.isHttpOnly()).isTrue(), + () -> assertThat(accessCookie.getPath()).isEqualTo("/")); + + Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); + assertThat(refreshCookie).isNotNull(); + assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), + () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), + () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); + } + + @DisplayName("토큰 redis 검증") + @Test + void valid_token_in_redis() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatNoException().isThrownBy(() -> + authorizationService.validTokenPair(1L, "token", Role.USER)); + } + + @DisplayName("이전 토큰과 동일한지 검증") + @Test + void valid_token_failby_notEquals_token() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken", Role.USER)).isInstanceOf( + UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); + verify(tokenRepository).delete(1L, Role.USER); + } + + @DisplayName("토큰 삭제 성공") + @Test + void error_with_expire_token(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.setCookies( + CookieUtils.tokenCookie("access_token", "value", 100000, domain), + CookieUtils.tokenCookie("refresh_token", "value", 100000, domain), + CookieUtils.typeCookie("Bearer", 100000, domain)); + + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + Cookie cookie = httpServletResponse.getCookie("access_token"); + + // Then + assertThat(cookie).isNotNull(); + assertThat(cookie.getMaxAge()).isZero(); + assertThat(cookie.getValue()).isEqualTo("value"); + + verify(tokenRepository).delete(authMember.id(), Role.USER); + } + + @DisplayName("토큰 없어서 삭제 실패") + @Test + void token_null_delete_fail(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + + // Then + assertThat(httpServletResponse.getCookies()).isEmpty(); + } + + @DisplayName("회원 탈퇴 요청 성공") + @Test + void unlink_success(@WithMember AuthMember authMember) { + // given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + doNothing().when(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); + } + + @DisplayName("회원이 없어서 찾기 실패") + @Test + void unlink_failBy_find_Member(@WithMember AuthMember authMember) { + // Given + When + willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) + .given(memberService) + .findMemberToDelete(authMember.id()); + + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(NotFoundException.class) + .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); + } + + @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") + @Test + void unlink_failBy_(@WithMember AuthMember authMember) { + // Given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + willThrow(BadRequestException.class) + .given(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); + } } diff --git a/src/test/java/com/moabam/api/application/auth/JwtAuthenticationServiceTest.java b/src/test/java/com/moabam/api/application/auth/JwtAuthenticationServiceTest.java index 9c56f174..37149fb2 100644 --- a/src/test/java/com/moabam/api/application/auth/JwtAuthenticationServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/JwtAuthenticationServiceTest.java @@ -16,8 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import com.moabam.api.application.auth.JwtAuthenticationService; -import com.moabam.api.application.auth.JwtProviderService; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; @@ -32,6 +31,7 @@ class JwtAuthenticationServiceTest { String originIss = "PARK"; String originSecretKey = "testestestestestestestestestesttestestestestestestestestestest"; + String adminKey = "testestestestestestestestestesttestestestestestestestestestest"; long originId = 1L; long originAccessExpire = 100000; long originRefreshExpire = 150000; @@ -42,7 +42,7 @@ class JwtAuthenticationServiceTest { @BeforeEach void initConfig() { - tokenConfig = new TokenConfig(originIss, originAccessExpire, originRefreshExpire, originSecretKey); + tokenConfig = new TokenConfig(originIss, originAccessExpire, originRefreshExpire, originSecretKey, adminKey); jwtProviderService = new JwtProviderService(tokenConfig); jwtAuthenticationService = new JwtAuthenticationService(tokenConfig); } @@ -55,7 +55,7 @@ void token_authentication_success() { // when, then assertThatNoException().isThrownBy(() -> - jwtAuthenticationService.isTokenExpire(token)); + jwtAuthenticationService.isTokenExpire(token, Role.USER)); } @DisplayName("토큰 인증 시간 만료 테스트") @@ -63,14 +63,14 @@ void token_authentication_success() { void token_authentication_time_expire() { // Given PublicClaim publicClaim = PublicClaimFixture.publicClaim(); - TokenConfig tokenConfig = new TokenConfig(originIss, 0, 0, originSecretKey); + TokenConfig tokenConfig = new TokenConfig(originIss, 0, 0, originSecretKey, adminKey); JwtAuthenticationService jwtAuthenticationService = new JwtAuthenticationService(tokenConfig); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); String token = jwtProviderService.provideAccessToken(publicClaim); // When assertThatNoException().isThrownBy(() -> { - boolean result = jwtAuthenticationService.isTokenExpire(token); + boolean result = jwtAuthenticationService.isTokenExpire(token, Role.USER); // Then assertThat(result).isTrue(); @@ -98,7 +98,7 @@ void token_authenticate_failBy_payload() { parts[2]); // Then - Assertions.assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(newToken)) + Assertions.assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(newToken, Role.USER)) .isInstanceOf(UnauthorizedException.class); } @@ -121,7 +121,7 @@ void token_authenticate_failBy_key() { .compact(); // When + Then - assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(token)) + assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(token, Role.USER)) .isExactlyInstanceOf(UnauthorizedException.class); } diff --git a/src/test/java/com/moabam/api/application/auth/JwtProviderServiceTest.java b/src/test/java/com/moabam/api/application/auth/JwtProviderServiceTest.java index 9c985f57..4a85f46a 100644 --- a/src/test/java/com/moabam/api/application/auth/JwtProviderServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/JwtProviderServiceTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.support.fixture.PublicClaimFixture; @@ -24,6 +25,7 @@ class JwtProviderServiceTest { String iss = "PARK"; String secretKey = "testestestestestestestestestesttestestestestestestestestestest"; + String adminKey = "testestestestestestestestestesttestestestestestestestestestest"; long id = 1L; @DisplayName("access 토큰 생성 성공") @@ -32,7 +34,7 @@ void create_access_token_success() throws JSONException { // given long accessExpire = 10000L; - TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey); + TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey, adminKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); PublicClaim publicClaim = PublicClaimFixture.publicClaim(); @@ -97,11 +99,11 @@ void create_refresh_token_success() throws JSONException { // given long refreshExpire = 15000L; - TokenConfig tokenConfig = new TokenConfig("PARK", 0L, refreshExpire, secretKey); + TokenConfig tokenConfig = new TokenConfig("PARK", 0L, refreshExpire, secretKey, adminKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); // when - String refreshToken = jwtProviderService.provideRefreshToken(); + String refreshToken = jwtProviderService.provideRefreshToken(Role.USER); String[] parts = refreshToken.split("\\."); String headers = new String(Base64.getDecoder().decode(parts[0])); @@ -128,7 +130,7 @@ void create_access_token_fail() { // given long accessExpire = -1L; - TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey); + TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey, adminKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); PublicClaim publicClaim = PublicClaimFixture.publicClaim(); @@ -149,7 +151,7 @@ void create_token_fail() { // given long refreshExpire = -1L; - TokenConfig tokenConfig = new TokenConfig("PARK", 0L, refreshExpire, secretKey); + TokenConfig tokenConfig = new TokenConfig("PARK", 0L, refreshExpire, secretKey, adminKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); PublicClaim publicClaim = PublicClaimFixture.publicClaim(); diff --git a/src/test/java/com/moabam/api/infrastructure/redis/TokenRepostiroyTest.java b/src/test/java/com/moabam/api/infrastructure/redis/TokenRepostiroyTest.java index d720226f..006d392e 100644 --- a/src/test/java/com/moabam/api/infrastructure/redis/TokenRepostiroyTest.java +++ b/src/test/java/com/moabam/api/infrastructure/redis/TokenRepostiroyTest.java @@ -15,6 +15,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import com.moabam.api.domain.auth.repository.TokenRepository; +import com.moabam.api.domain.member.Role; import com.moabam.api.dto.auth.TokenSaveValue; import com.moabam.support.fixture.TokenSaveValueFixture; @@ -35,7 +36,7 @@ void save_token_suceess() { // When + Then Assertions.assertThatNoException() - .isThrownBy(() -> tokenRepository.saveToken(1L, TokenSaveValueFixture.tokenSaveValue())); + .isThrownBy(() -> tokenRepository.saveToken(1L, TokenSaveValueFixture.tokenSaveValue(), Role.USER)); } @DisplayName("토큰 조회 성공") @@ -46,7 +47,7 @@ void token_get_success() { .given(hashRedisRepository).get(anyString()); // when - TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(123L); + TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(123L, Role.USER); // then assertAll( @@ -60,6 +61,6 @@ void token_get_success() { void delete_token_suceess() { // When + Then Assertions.assertThatNoException() - .isThrownBy(() -> tokenRepository.delete(1L)); + .isThrownBy(() -> tokenRepository.delete(1L, Role.USER)); } } diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 6297567c..e07a6ff8 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -58,6 +58,7 @@ import com.moabam.api.domain.member.Badge; import com.moabam.api.domain.member.BadgeType; import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.Role; import com.moabam.api.domain.member.repository.BadgeRepository; import com.moabam.api.domain.member.repository.MemberRepository; import com.moabam.api.domain.member.repository.MemberSearchRepository; @@ -167,14 +168,14 @@ void setUp() { void logout_success() throws Exception { // given TokenSaveValue tokenSaveValue = TokenSaveValueFixture.tokenSaveValue(); - tokenRepository.saveToken(member.getId(), tokenSaveValue); + tokenRepository.saveToken(member.getId(), tokenSaveValue, Role.USER); // expected ResultActions result = mockMvc.perform(get("/members/logout")); result.andExpect(status().is2xxSuccessful()); - Assertions.assertThatThrownBy(() -> tokenRepository.getTokenSaveValue(member.getId())) + Assertions.assertThatThrownBy(() -> tokenRepository.getTokenSaveValue(member.getId(), Role.USER)) .isInstanceOf(UnauthorizedException.class); } diff --git a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java index f9b866d4..9ef0d24c 100644 --- a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java @@ -1,11 +1,12 @@ package com.moabam.api.presentation; -import static org.hamcrest.Matchers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.util.ArrayList; -import java.util.List; - +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.MemberFixture; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -18,13 +19,13 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.transaction.annotation.Transactional; -import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.UpdateRanking; -import com.moabam.support.annotation.WithMember; -import com.moabam.support.common.WithoutFilterSupporter; -import com.moabam.support.fixture.MemberFixture; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Transactional @SpringBootTest @@ -32,57 +33,57 @@ @AutoConfigureRestDocs class RankingControllerTest extends WithoutFilterSupporter { - @Autowired - MockMvc mockMvc; - - @Autowired - MemberRepository memberRepository; - - @Autowired - RedisTemplate redisTemplate; - - @BeforeEach - void init() { - redisTemplate.delete("Ranking"); - } - - @DisplayName("") - @WithMember - @Test - void top_ranking() throws Exception { - // given - List members = new ArrayList<>(); - for (int i = 0; i < 20; i++) { - Member member = MemberFixture.member(String.valueOf(i + 1)); - members.add(member); - - RankingInfo rankingInfo = new RankingInfo((long)(i + 1), member.getNickname(), member.getProfileImage()); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); - } - memberRepository.saveAll(members); - - RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); - RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); - - UpdateRanking myRanking = UpdateRanking.builder() - .score(1L) - .rankingInfo(RankingInfo.builder() - .nickname(members.get(0).getNickname()) - .memberId(members.get(0).getId()) - .image(members.get(0).getProfileImage()).build()) - .build(); - - // when - mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.topRankings", hasSize(10))) - .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) - .andExpect(jsonPath("$.myRanking.rank", is(21))); - - // then - - } + @Autowired + MockMvc mockMvc; + + @Autowired + MemberRepository memberRepository; + + @Autowired + RedisTemplate redisTemplate; + + @BeforeEach + void init() { + redisTemplate.delete("Ranking"); + } + + @DisplayName("") + @WithMember + @Test + void top_ranking() throws Exception { + // given + List members = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + Member member = MemberFixture.member(String.valueOf(i + 1)); + members.add(member); + + RankingInfo rankingInfo = new RankingInfo((long) (i + 1), member.getNickname(), member.getProfileImage()); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); + } + memberRepository.saveAll(members); + + RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); + RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); + + UpdateRanking myRanking = UpdateRanking.builder() + .score(1L) + .rankingInfo(RankingInfo.builder() + .nickname(members.get(0).getNickname()) + .memberId(members.get(0).getId()) + .image(members.get(0).getProfileImage()).build()) + .build(); + + // when + mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topRankings", hasSize(10))) + .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) + .andExpect(jsonPath("$.myRanking.rank", is(22))); + + // then + + } } diff --git a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java index dbca7ec2..613838d2 100644 --- a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java @@ -1,29 +1,5 @@ package com.moabam.api.presentation; -import static com.moabam.api.domain.room.RoomType.*; -import static org.assertj.core.api.Assertions.*; -import static org.springframework.http.MediaType.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.transaction.annotation.Transactional; - import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; @@ -31,30 +7,39 @@ import com.moabam.api.domain.item.repository.ItemRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.domain.room.Certification; -import com.moabam.api.domain.room.DailyMemberCertification; -import com.moabam.api.domain.room.DailyRoomCertification; -import com.moabam.api.domain.room.Participant; -import com.moabam.api.domain.room.Room; -import com.moabam.api.domain.room.RoomType; -import com.moabam.api.domain.room.Routine; -import com.moabam.api.domain.room.repository.CertificationRepository; -import com.moabam.api.domain.room.repository.DailyMemberCertificationRepository; -import com.moabam.api.domain.room.repository.DailyRoomCertificationRepository; -import com.moabam.api.domain.room.repository.ParticipantRepository; -import com.moabam.api.domain.room.repository.ParticipantSearchRepository; -import com.moabam.api.domain.room.repository.RoomRepository; -import com.moabam.api.domain.room.repository.RoutineRepository; +import com.moabam.api.domain.room.*; +import com.moabam.api.domain.room.repository.*; import com.moabam.api.dto.room.CreateRoomRequest; import com.moabam.api.dto.room.EnterRoomRequest; import com.moabam.api.dto.room.ModifyRoomRequest; +import com.moabam.global.common.util.SystemClockHolder; import com.moabam.support.annotation.WithMember; import com.moabam.support.common.WithoutFilterSupporter; -import com.moabam.support.fixture.BugFixture; -import com.moabam.support.fixture.InventoryFixture; -import com.moabam.support.fixture.ItemFixture; -import com.moabam.support.fixture.MemberFixture; -import com.moabam.support.fixture.RoomFixture; +import com.moabam.support.fixture.*; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.BDDMockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static com.moabam.api.domain.room.RoomType.MORNING; +import static com.moabam.api.domain.room.RoomType.NIGHT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Transactional @SpringBootTest @@ -62,1531 +47,1535 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class RoomControllerTest extends WithoutFilterSupporter { - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private RoomRepository roomRepository; - - @Autowired - private RoutineRepository routineRepository; - - @Autowired - private ParticipantRepository participantRepository; - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private CertificationRepository certificationRepository; - - @Autowired - private DailyMemberCertificationRepository dailyMemberCertificationRepository; - - @Autowired - private DailyRoomCertificationRepository dailyRoomCertificationRepository; - - @Autowired - private ParticipantSearchRepository participantSearchRepository; - - @Autowired - private ItemRepository itemRepository; - - @Autowired - private InventoryRepository inventoryRepository; - - Member member; - - @BeforeAll - void setUp() { - member = MemberFixture.member(); - memberRepository.save(member); - } - - @AfterEach - void cleanUp() { - while (member.getCurrentMorningCount() > 0) { - member.exitRoom(MORNING); - } - - while (member.getCurrentNightCount() > 0) { - member.exitRoom(NIGHT); - } - } - - @DisplayName("비밀번호 없는 방 생성 성공") - @WithMember - @Test - void create_room_no_password_success() throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isCreated()) - .andDo(print()); - - assertThat(roomRepository.findAll()).hasSize(1); - assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("재윤과 앵맹이의 방임"); - assertThat(roomRepository.findAll().get(0).getPassword()).isNull(); - } - - @DisplayName("비밀번호 있는 방 생성 성공") - @WithMember - @ParameterizedTest - @CsvSource({ - "1234", "12345678", "98765" - }) - void create_room_with_password_success(String password) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 있는 재맹의 방임", password, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isCreated()) - .andDo(print()); - - assertThat(roomRepository.findAll()).hasSize(1); - assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("비번 있는 재맹의 방임"); - assertThat(roomRepository.findAll().get(0).getPassword()).isEqualTo(password); - } - - @DisplayName("올바르지 않은 비밀번호 방 생성시 예외 발생") - @ParameterizedTest - @CsvSource({ - "1", "12", "123", "123456789", "abc" - }) - void create_room_with_wrong_password_fail(String password) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 있는 재윤과 앵맹이의 방임", password, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("Routine 갯수를 초과한 방 생성시 예외 발생") - @Test - void create_room_with_too_many_routine_fail() throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - routines.add("밥 먹기"); - routines.add("코드 리뷰 달기"); - routines.add("책 읽기"); - routines.add("산책 하기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("Routine 없는 방 생성시 예외 발생") - @Test - void create_room_with_no_routine_fail() throws Exception { - // given - List routines = new ArrayList<>(); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("올바르지 못한 시간으로 아침 방 생성시 예외 발생") - @ParameterizedTest - @CsvSource({ - "1", "3", "11", "12", "20" - }) - void create_morning_room_wrong_certify_time_fail(int certifyTime) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, certifyTime, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("올바르지 못한 시간으로 저녁 방 생성시 에외 발생") - @ParameterizedTest - @CsvSource({ - "19", "3", "6", "9" - }) - void create_night_room_wrong_certify_time_fail(int certifyTime) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, NIGHT, certifyTime, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("방 수정 성공 - 방장일 경우") - @WithMember(id = 1L) - @Test - void modify_room_success() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - List routines = RoomFixture.routines(room); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - roomRepository.save(room); - routineRepository.saveAll(routines); - participantRepository.save(participant); - - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "공지공지", "4567", 10, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - - Room modifiedRoom = roomRepository.findById(room.getId()).orElseThrow(); - List modifiedRoutines = routineRepository.findAllByRoomId(room.getId()); - - assertThat(modifiedRoom.getTitle()).isEqualTo("수정할 방임!"); - assertThat(modifiedRoom.getCertifyTime()).isEqualTo(10); - assertThat(modifiedRoom.getPassword()).isEqualTo("4567"); - assertThat(modifiedRoom.getAnnouncement()).isEqualTo("공지공지"); - assertThat(modifiedRoom.getMaxUserCount()).isEqualTo(7); - assertThat(modifiedRoutines).hasSize(2); - } - - @DisplayName("방 수정 실패 - 방장 아닐 경우") - @WithMember(id = 1L) - @Test - void unauthorized_modify_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - roomRepository.save(room); - participantRepository.save(participant); - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 9, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - String message = "{\"message\":\"방장이 아닌 사용자는 방을 수정할 수 없습니다.\"}"; - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isNotFound()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("방 수정 실패 - 이미 한 참여자가 인증하고 방의 인증 시간을 바꾸려고 할때 예외 처리") - @WithMember(id = 1L) - @Test - void room_certify_time_modification_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - room = roomRepository.save(room); - - Member member2 = MemberFixture.member("12313123"); - member2 = memberRepository.save(member2); - - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - - Participant participant2 = RoomFixture.participant(room, member2.getId()); - - participantRepository.saveAll(List.of(participant1, participant2)); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member2.getId(), - room.getId(), participant2); - - dailyMemberCertificationRepository.save(dailyMemberCertification); - - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 10, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("비밀번호 있는 방 참여 성공") - @WithMember(id = 1L) - @Test - void enter_room_with_password_success() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("비밀번호 없는 방 참여 성공") - @WithMember(id = 1L) - @Test - void enter_room_with_no_password_success() throws Exception { - // given - Room room = RoomFixture.room(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest(null); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 참여 후 인원수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_room_user_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); - - // then - assertThat(findRoom.getCurrentUserCount()).isEqualTo(2); - } - - @DisplayName("아침 방 참여 후 사용자의 방 입장 횟수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_morning_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentMorningCount()).isEqualTo(1); - assertThat(getMember.getCurrentNightCount()).isZero(); - } - - @DisplayName("저녁 방 참여 후 사용자의 방 입장 횟수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_night_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentNightCount()).isEqualTo(1); - assertThat(getMember.getCurrentMorningCount()).isZero(); - } - - @DisplayName("사용자의 아침 방 입장 횟수 3일시 예외 처리") - @Test - void enter_and_morning_room_over_three_fail() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 3; i++) { - member.enterRoom(MORNING); - } - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()); - } - - @DisplayName("사용자의 저녁 방 입장 횟수 3일시 예외 처리") - @Test - void enter_and_night_room_over_three_fail() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(22) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 3; i++) { - member.enterRoom(NIGHT); - } - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()); - } - - @DisplayName("비밀번호 불일치 방 참여시 예외 발생") - @WithMember(id = 1L) - @Test - void enter_room_wrong_password_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - Member member = Member.builder() - .id(1L) - .socialId("1") - .bug(BugFixture.bug()) - .build(); - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - String message = "{\"message\":\"방의 비밀번호가 일치하지 않습니다.\"}"; - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("인원수가 모두 찬 방 참여시 예외 발생") - @WithMember(id = 1L) - @Test - void enter_max_user_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 4; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - String message = "{\"message\":\"방의 인원수가 찼습니다.\"}"; - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("일반 사용자의 방 나가기 성공") - @WithMember(id = 1L) - @Test - void no_manager_exit_room_success() throws Exception { - // given - Room room = Room.builder() - .title("5명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(8) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 4; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - participantRepository.save(participant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - - Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); - List deletedParticipant = participantRepository.findAll(); - - assertThat(findRoom.getCurrentUserCount()).isEqualTo(4); - assertThat(deletedParticipant).hasSize(1); - assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); - assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); - } - - @DisplayName("방장의 방 나가기 - 방 삭제 성공") - @WithMember(id = 1L) - @Test - void manager_delete_room_success() throws Exception { - // given - Room room = Room.builder() - .title("1명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(8) - .build(); - - List routines = RoomFixture.routines(room); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - roomRepository.save(room); - routineRepository.saveAll(routines); - participantRepository.save(participant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - - List deletedRoom = roomRepository.findAll(); - List deletedRoutine = routineRepository.findAll(); - List deletedParticipant = participantRepository.findAll(); - - assertThat(deletedRoom).isEmpty(); - assertThat(deletedRoutine).hasSize(0); - assertThat(deletedParticipant).hasSize(1); - assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); - assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); - } - - @DisplayName("방장이 위임하지 않고 방 나가기 실패") - @WithMember(id = 1L) - @Test - void manager_exit_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("7명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(10) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - for (int i = 0; i < 6; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - participantRepository.save(participant); - String message = "{\"message\":\"인원수가 2명 이상일 때는 방장을 위임해야 합니다.\"}"; - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("아침 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") - @WithMember(id = 1L) - @Test - void exit_and_decrease_morning_room_count() throws Exception { - // given - Room room = RoomFixture.room(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 3; i++) { - member.enterRoom(RoomType.MORNING); - } - - memberRepository.save(member); - roomRepository.save(room); - participantRepository.save(participant); - - // when - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentMorningCount()).isEqualTo(2); - } - - @DisplayName("저녁 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") - @WithMember(id = 1L) - @Test - void exit_and_decrease_night_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(23) - .maxUserCount(5) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 3; i++) { - member.enterRoom(NIGHT); - } - - memberRepository.save(member); - roomRepository.save(room); - participantRepository.save(participant); - - // when - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentNightCount()).isEqualTo(2); - } - - @DisplayName("방 상세 정보 조회 성공 테스트") - @WithMember(id = 1L) - @Test - void get_room_details_test() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(23) - .maxUserCount(5) - .build(); - - room.increaseCurrentUserCount(); - room.increaseCurrentUserCount(); - - List routines = RoomFixture.routines(room); - - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - - Member member2 = MemberFixture.member("2"); - Member member3 = MemberFixture.member("3"); - - roomRepository.save(room); - routineRepository.saveAll(routines); - member2 = memberRepository.save(member2); - member3 = memberRepository.save(member3); - - Item item = ItemFixture.nightMageSkin(); - - Inventory inventory1 = InventoryFixture.inventory(1L, item); - Inventory inventory2 = InventoryFixture.inventory(member2.getId(), item); - Inventory inventory3 = InventoryFixture.inventory(member3.getId(), item); - inventory1.select(member); - inventory2.select(member2); - inventory3.select(member3); - - itemRepository.save(item); - inventoryRepository.saveAll(List.of(inventory1, inventory2, inventory3)); - - Participant participant2 = RoomFixture.participant(room, member2.getId()); - Participant participant3 = RoomFixture.participant(room, member3.getId()); - - participantRepository.save(participant1); - participantRepository.save(participant2); - participantRepository.save(participant3); - - Certification certification1 = Certification.builder() - .routine(routines.get(0)) - .memberId(member.getId()) - .image("member1Image") - .build(); - - Certification certification2 = Certification.builder() - .routine(routines.get(1)) - .memberId(member.getId()) - .image("member2Image") - .build(); - - certificationRepository.save(certification1); - certificationRepository.save(certification2); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member.getId(), - room.getId(), participant1); - dailyMemberCertificationRepository.save(dailyMemberCertification); - - DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(room.getId(), - LocalDate.now()); - dailyRoomCertificationRepository.save(dailyRoomCertification); - - DailyRoomCertification dailyRoomCertification1 = RoomFixture.dailyRoomCertification(room.getId(), - LocalDate.now().minusDays(3)); - dailyRoomCertificationRepository.save(dailyRoomCertification1); - - // expected - mockMvc.perform(get("/rooms/" + room.getId() + "/" + LocalDate.now())) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 추방 성공") - @WithMember(id = 1L) - @Test - void deport_member_success() throws Exception { - // given - Room room = RoomFixture.room(); - Member member = MemberFixture.member("1234"); - memberRepository.save(member); - - Participant memberParticipant = RoomFixture.participant(room, member.getId()); - Participant managerParticipant = RoomFixture.participant(room, 1L); - managerParticipant.enableManager(); - - room.increaseCurrentUserCount(); - - roomRepository.save(room); - participantRepository.save(memberParticipant); - participantRepository.save(managerParticipant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) - .andExpect(status().isOk()) - .andDo(print()); - roomRepository.flush(); - - Room getRoom = roomRepository.findById(room.getId()).orElseThrow(); - Participant getMemberParticipant = participantRepository.findById(memberParticipant.getId()).orElseThrow(); - - assertThat(getRoom.getCurrentUserCount()).isEqualTo(1); - assertThat(getMemberParticipant.getDeletedAt()).isNotNull(); - assertThat(participantSearchRepository.findOne(member.getId(), room.getId())).isEmpty(); - } - - @DisplayName("방장 본인 추방 시도 - 예외 처리") - @WithMember(id = 1L) - @Test - void deport_self_fail() throws Exception { - // given - Room room = RoomFixture.room(); - - Participant managerParticipant = RoomFixture.participant(room, member.getId()); - managerParticipant.enableManager(); - - roomRepository.save(room); - participantRepository.save(managerParticipant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("방장 위임 성공") - @WithMember(id = 1L) - @Test - void mandate_manager_success() throws Exception { - // given - Member member2 = MemberFixture.member("1234"); - memberRepository.save(member2); - - Room room = RoomFixture.room(); - Participant participant1 = RoomFixture.participant(room, member.getId()); - participant1.enableManager(); - Participant participant2 = RoomFixture.participant(room, member2.getId()); - - roomRepository.save(room); - participantRepository.save(participant1); - participantRepository.save(participant2); - - // expected - mockMvc.perform(put("/rooms/" + room.getId() + "/members/" + member2.getId() + "/mandate")) - .andExpect(status().isOk()) - .andDo(print()); - - Room savedRoom = roomRepository.findById(room.getId()).orElseThrow(); - Participant savedParticipant1 = participantRepository.findById(participant1.getId()).orElseThrow(); - Participant savedParticipant2 = participantRepository.findById(participant2.getId()).orElseThrow(); - - assertThat(savedRoom.getManagerNickname()).isEqualTo(member2.getNickname()); - assertThat(savedParticipant1.isManager()).isFalse(); - assertThat(savedParticipant2.isManager()).isTrue(); - } - - @DisplayName("현재 참여중인 모든 방 조회 성공 - 첫번째 방은 개인과 방 모두 인증 성공") - @WithMember(id = 1L) - @Test - void get_all_my_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); - Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); - Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); - - Participant participant1 = RoomFixture.participant(room1, 1L); - Participant participant2 = RoomFixture.participant(room2, 1L); - Participant participant3 = RoomFixture.participant(room3, 1L); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(1L, 1L, participant1); - DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(1L, LocalDate.now()); - - roomRepository.saveAll(List.of(room1, room2, room3)); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - dailyMemberCertificationRepository.save(dailyMemberCertification); - dailyRoomCertificationRepository.save(dailyRoomCertification); - - // expected - mockMvc.perform(get("/rooms/my-join")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 참여 기록 조회 성공") - @WithMember(id = 1L) - @Test - void get_join_history_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); - Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); - Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); - - Participant participant1 = RoomFixture.participant(room1, 1L); - Participant participant2 = RoomFixture.participant(room2, 1L); - Participant participant3 = RoomFixture.participant(room3, 1L); - - roomRepository.saveAll(List.of(room1, room2, room3)); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - - participant3.removeRoom(); - participantRepository.flush(); - participantRepository.delete(participant3); - - // expected - mockMvc.perform(get("/rooms/join-history")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중이지 않은 방에 대한 확인 성공") - @WithMember - @Test - void check_if_participant_false_success() throws Exception { - // given - Room room = RoomFixture.room(); - Room savedRoom = roomRepository.save(room); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중이지 않은 방의 정보 불러오기 성공") - @Test - void get_un_joined_room_details() throws Exception { - // given - Room room = RoomFixture.room("테스트 방", NIGHT, 21); - Room savedRoom = roomRepository.save(room); - - Member member1 = MemberFixture.member("901010"); - member1 = memberRepository.save(member1); - - Item item = ItemFixture.nightMageSkin(); - - Inventory inventory = InventoryFixture.inventory(member1.getId(), item); - inventory.select(member1); - - itemRepository.save(item); - inventoryRepository.save(inventory); - - Participant participant = RoomFixture.participant(savedRoom, member1.getId()); - participantRepository.save(participant); - - Routine routine1 = RoomFixture.routine(savedRoom, "물 마시기"); - Routine routine2 = RoomFixture.routine(savedRoom, "커피 마시기"); - routineRepository.saveAll(List.of(routine1, routine2)); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member1.getId(), - savedRoom.getId(), participant); - dailyMemberCertificationRepository.save(dailyMemberCertification); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/un-joined")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중인 방에 대한 확인 성공") - @WithMember - @Test - void check_if_participant_true_success() throws Exception { - // given - Room room = RoomFixture.room(); - Room savedRoom = roomRepository.save(room); - - Participant participant = RoomFixture.participant(room, 1L); - participantRepository.save(participant); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침, 저녁 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 있음") - @WithMember(id = 1L) - @Test - void search_all_morning_night_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침, 저녁 방 전체 조회 성공 - 마지막 조회, 다음 페이지 없음") - @WithMember(id = 1L) - @Test - void search_last_page_all_morning_night_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RoomRepository roomRepository; + + @Autowired + private RoutineRepository routineRepository; + + @Autowired + private ParticipantRepository participantRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private CertificationRepository certificationRepository; + + @Autowired + private DailyMemberCertificationRepository dailyMemberCertificationRepository; + + @Autowired + private DailyRoomCertificationRepository dailyRoomCertificationRepository; + + @Autowired + private ParticipantSearchRepository participantSearchRepository; + + @Autowired + private ItemRepository itemRepository; + + @Autowired + private InventoryRepository inventoryRepository; + + @SpyBean + private SystemClockHolder clockHolder; + + Member member; + + @BeforeAll + void setUp() { + member = MemberFixture.member(); + memberRepository.save(member); + } + + @AfterEach + void cleanUp() { + while (member.getCurrentMorningCount() > 0) { + member.exitRoom(MORNING); + } + + while (member.getCurrentNightCount() > 0) { + member.exitRoom(NIGHT); + } + } + + @DisplayName("비밀번호 없는 방 생성 성공") + @WithMember + @Test + void create_room_no_password_success() throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isCreated()) + .andDo(print()); + + assertThat(roomRepository.findAll()).hasSize(1); + assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("재윤과 앵맹이의 방임"); + assertThat(roomRepository.findAll().get(0).getPassword()).isNull(); + } + + @DisplayName("비밀번호 있는 방 생성 성공") + @WithMember + @ParameterizedTest + @CsvSource({ + "1234", "12345678", "98765" + }) + void create_room_with_password_success(String password) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 있는 재맹의 방임", password, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isCreated()) + .andDo(print()); + + assertThat(roomRepository.findAll()).hasSize(1); + assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("비번 있는 재맹의 방임"); + assertThat(roomRepository.findAll().get(0).getPassword()).isEqualTo(password); + } + + @DisplayName("올바르지 않은 비밀번호 방 생성시 예외 발생") + @ParameterizedTest + @CsvSource({ + "1", "12", "123", "123456789", "abc" + }) + void create_room_with_wrong_password_fail(String password) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 있는 재윤과 앵맹이의 방임", password, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("Routine 갯수를 초과한 방 생성시 예외 발생") + @Test + void create_room_with_too_many_routine_fail() throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + routines.add("밥 먹기"); + routines.add("코드 리뷰 달기"); + routines.add("책 읽기"); + routines.add("산책 하기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("Routine 없는 방 생성시 예외 발생") + @Test + void create_room_with_no_routine_fail() throws Exception { + // given + List routines = new ArrayList<>(); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("올바르지 못한 시간으로 아침 방 생성시 예외 발생") + @ParameterizedTest + @CsvSource({ + "1", "3", "11", "12", "20" + }) + void create_morning_room_wrong_certify_time_fail(int certifyTime) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, certifyTime, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("올바르지 못한 시간으로 저녁 방 생성시 에외 발생") + @ParameterizedTest + @CsvSource({ + "19", "3", "6", "9" + }) + void create_night_room_wrong_certify_time_fail(int certifyTime) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, NIGHT, certifyTime, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("방 수정 성공 - 방장일 경우") + @WithMember(id = 1L) + @Test + void modify_room_success() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + List routines = RoomFixture.routines(room); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + roomRepository.save(room); + routineRepository.saveAll(routines); + participantRepository.save(participant); + + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "공지공지", "4567", 10, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + + Room modifiedRoom = roomRepository.findById(room.getId()).orElseThrow(); + List modifiedRoutines = routineRepository.findAllByRoomId(room.getId()); + + assertThat(modifiedRoom.getTitle()).isEqualTo("수정할 방임!"); + assertThat(modifiedRoom.getCertifyTime()).isEqualTo(10); + assertThat(modifiedRoom.getPassword()).isEqualTo("4567"); + assertThat(modifiedRoom.getAnnouncement()).isEqualTo("공지공지"); + assertThat(modifiedRoom.getMaxUserCount()).isEqualTo(7); + assertThat(modifiedRoutines).hasSize(2); + } + + @DisplayName("방 수정 실패 - 방장 아닐 경우") + @WithMember(id = 1L) + @Test + void unauthorized_modify_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + roomRepository.save(room); + participantRepository.save(participant); + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 9, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + String message = "{\"message\":\"방장이 아닌 사용자는 방을 수정할 수 없습니다.\"}"; + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isNotFound()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("방 수정 실패 - 이미 한 참여자가 인증하고 방의 인증 시간을 바꾸려고 할때 예외 처리") + @WithMember(id = 1L) + @Test + void room_certify_time_modification_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + room = roomRepository.save(room); + + Member member2 = MemberFixture.member("12313123"); + member2 = memberRepository.save(member2); + + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + + Participant participant2 = RoomFixture.participant(room, member2.getId()); + + participantRepository.saveAll(List.of(participant1, participant2)); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member2.getId(), + room.getId(), participant2); + + dailyMemberCertificationRepository.save(dailyMemberCertification); + + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 10, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("비밀번호 있는 방 참여 성공") + @WithMember(id = 1L) + @Test + void enter_room_with_password_success() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("비밀번호 없는 방 참여 성공") + @WithMember(id = 1L) + @Test + void enter_room_with_no_password_success() throws Exception { + // given + BDDMockito.given(clockHolder.times()).willReturn(LocalDateTime.of(2023, 12, 3, 14, 30, 0)); + Room room = RoomFixture.room(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest(null); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 참여 후 인원수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_room_user_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); + + // then + assertThat(findRoom.getCurrentUserCount()).isEqualTo(2); + } + + @DisplayName("아침 방 참여 후 사용자의 방 입장 횟수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_morning_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentMorningCount()).isEqualTo(1); + assertThat(getMember.getCurrentNightCount()).isZero(); + } + + @DisplayName("저녁 방 참여 후 사용자의 방 입장 횟수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_night_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentNightCount()).isEqualTo(1); + assertThat(getMember.getCurrentMorningCount()).isZero(); + } + + @DisplayName("사용자의 아침 방 입장 횟수 3일시 예외 처리") + @Test + void enter_and_morning_room_over_three_fail() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 3; i++) { + member.enterRoom(MORNING); + } + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()); + } + + @DisplayName("사용자의 저녁 방 입장 횟수 3일시 예외 처리") + @Test + void enter_and_night_room_over_three_fail() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(22) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 3; i++) { + member.enterRoom(NIGHT); + } + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()); + } + + @DisplayName("비밀번호 불일치 방 참여시 예외 발생") + @WithMember(id = 1L) + @Test + void enter_room_wrong_password_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + Member member = Member.builder() + .id(1L) + .socialId("1") + .bug(BugFixture.bug()) + .build(); + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + String message = "{\"message\":\"방의 비밀번호가 일치하지 않습니다.\"}"; + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("인원수가 모두 찬 방 참여시 예외 발생") + @WithMember(id = 1L) + @Test + void enter_max_user_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 4; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + String message = "{\"message\":\"방의 인원수가 찼습니다.\"}"; + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("일반 사용자의 방 나가기 성공") + @WithMember(id = 1L) + @Test + void no_manager_exit_room_success() throws Exception { + // given + Room room = Room.builder() + .title("5명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(8) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 4; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + participantRepository.save(participant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + + Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); + List deletedParticipant = participantRepository.findAll(); + + assertThat(findRoom.getCurrentUserCount()).isEqualTo(4); + assertThat(deletedParticipant).hasSize(1); + assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); + assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); + } + + @DisplayName("방장의 방 나가기 - 방 삭제 성공") + @WithMember(id = 1L) + @Test + void manager_delete_room_success() throws Exception { + // given + Room room = Room.builder() + .title("1명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(8) + .build(); + + List routines = RoomFixture.routines(room); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + roomRepository.save(room); + routineRepository.saveAll(routines); + participantRepository.save(participant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + + List deletedRoom = roomRepository.findAll(); + List deletedRoutine = routineRepository.findAll(); + List deletedParticipant = participantRepository.findAll(); + + assertThat(deletedRoom).isEmpty(); + assertThat(deletedRoutine).hasSize(0); + assertThat(deletedParticipant).hasSize(1); + assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); + assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); + } + + @DisplayName("방장이 위임하지 않고 방 나가기 실패") + @WithMember(id = 1L) + @Test + void manager_exit_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("7명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(10) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + for (int i = 0; i < 6; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + participantRepository.save(participant); + String message = "{\"message\":\"인원수가 2명 이상일 때는 방장을 위임해야 합니다.\"}"; + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("아침 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") + @WithMember(id = 1L) + @Test + void exit_and_decrease_morning_room_count() throws Exception { + // given + Room room = RoomFixture.room(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 3; i++) { + member.enterRoom(RoomType.MORNING); + } + + memberRepository.save(member); + roomRepository.save(room); + participantRepository.save(participant); + + // when + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentMorningCount()).isEqualTo(2); + } + + @DisplayName("저녁 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") + @WithMember(id = 1L) + @Test + void exit_and_decrease_night_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(23) + .maxUserCount(5) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 3; i++) { + member.enterRoom(NIGHT); + } + + memberRepository.save(member); + roomRepository.save(room); + participantRepository.save(participant); + + // when + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentNightCount()).isEqualTo(2); + } + + @DisplayName("방 상세 정보 조회 성공 테스트") + @WithMember(id = 1L) + @Test + void get_room_details_test() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(23) + .maxUserCount(5) + .build(); + + room.increaseCurrentUserCount(); + room.increaseCurrentUserCount(); + + List routines = RoomFixture.routines(room); + + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + + Member member2 = MemberFixture.member("2"); + Member member3 = MemberFixture.member("3"); + + roomRepository.save(room); + routineRepository.saveAll(routines); + member2 = memberRepository.save(member2); + member3 = memberRepository.save(member3); + + Item item = ItemFixture.nightMageSkin(); + + Inventory inventory1 = InventoryFixture.inventory(1L, item); + Inventory inventory2 = InventoryFixture.inventory(member2.getId(), item); + Inventory inventory3 = InventoryFixture.inventory(member3.getId(), item); + inventory1.select(member); + inventory2.select(member2); + inventory3.select(member3); + + itemRepository.save(item); + inventoryRepository.saveAll(List.of(inventory1, inventory2, inventory3)); + + Participant participant2 = RoomFixture.participant(room, member2.getId()); + Participant participant3 = RoomFixture.participant(room, member3.getId()); + + participantRepository.save(participant1); + participantRepository.save(participant2); + participantRepository.save(participant3); + + Certification certification1 = Certification.builder() + .routine(routines.get(0)) + .memberId(member.getId()) + .image("member1Image") + .build(); + + Certification certification2 = Certification.builder() + .routine(routines.get(1)) + .memberId(member.getId()) + .image("member2Image") + .build(); + + certificationRepository.save(certification1); + certificationRepository.save(certification2); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member.getId(), + room.getId(), participant1); + dailyMemberCertificationRepository.save(dailyMemberCertification); + + DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(room.getId(), + LocalDate.now()); + dailyRoomCertificationRepository.save(dailyRoomCertification); + + DailyRoomCertification dailyRoomCertification1 = RoomFixture.dailyRoomCertification(room.getId(), + LocalDate.now().minusDays(3)); + dailyRoomCertificationRepository.save(dailyRoomCertification1); + + // expected + mockMvc.perform(get("/rooms/" + room.getId() + "/" + LocalDate.now())) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 추방 성공") + @WithMember(id = 1L) + @Test + void deport_member_success() throws Exception { + // given + Room room = RoomFixture.room(); + Member member = MemberFixture.member("1234"); + memberRepository.save(member); + + Participant memberParticipant = RoomFixture.participant(room, member.getId()); + Participant managerParticipant = RoomFixture.participant(room, 1L); + managerParticipant.enableManager(); + + room.increaseCurrentUserCount(); + + roomRepository.save(room); + participantRepository.save(memberParticipant); + participantRepository.save(managerParticipant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) + .andExpect(status().isOk()) + .andDo(print()); + roomRepository.flush(); + + Room getRoom = roomRepository.findById(room.getId()).orElseThrow(); + Participant getMemberParticipant = participantRepository.findById(memberParticipant.getId()).orElseThrow(); + + assertThat(getRoom.getCurrentUserCount()).isEqualTo(1); + assertThat(getMemberParticipant.getDeletedAt()).isNotNull(); + assertThat(participantSearchRepository.findOne(member.getId(), room.getId())).isEmpty(); + } + + @DisplayName("방장 본인 추방 시도 - 예외 처리") + @WithMember(id = 1L) + @Test + void deport_self_fail() throws Exception { + // given + Room room = RoomFixture.room(); + + Participant managerParticipant = RoomFixture.participant(room, member.getId()); + managerParticipant.enableManager(); + + roomRepository.save(room); + participantRepository.save(managerParticipant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("방장 위임 성공") + @WithMember(id = 1L) + @Test + void mandate_manager_success() throws Exception { + // given + Member member2 = MemberFixture.member("1234"); + memberRepository.save(member2); + + Room room = RoomFixture.room(); + Participant participant1 = RoomFixture.participant(room, member.getId()); + participant1.enableManager(); + Participant participant2 = RoomFixture.participant(room, member2.getId()); + + roomRepository.save(room); + participantRepository.save(participant1); + participantRepository.save(participant2); + + // expected + mockMvc.perform(put("/rooms/" + room.getId() + "/members/" + member2.getId() + "/mandate")) + .andExpect(status().isOk()) + .andDo(print()); + + Room savedRoom = roomRepository.findById(room.getId()).orElseThrow(); + Participant savedParticipant1 = participantRepository.findById(participant1.getId()).orElseThrow(); + Participant savedParticipant2 = participantRepository.findById(participant2.getId()).orElseThrow(); + + assertThat(savedRoom.getManagerNickname()).isEqualTo(member2.getNickname()); + assertThat(savedParticipant1.isManager()).isFalse(); + assertThat(savedParticipant2.isManager()).isTrue(); + } + + @DisplayName("현재 참여중인 모든 방 조회 성공 - 첫번째 방은 개인과 방 모두 인증 성공") + @WithMember(id = 1L) + @Test + void get_all_my_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); + Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); + Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); + + Participant participant1 = RoomFixture.participant(room1, 1L); + Participant participant2 = RoomFixture.participant(room2, 1L); + Participant participant3 = RoomFixture.participant(room3, 1L); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(1L, 1L, participant1); + DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(1L, LocalDate.now()); + + roomRepository.saveAll(List.of(room1, room2, room3)); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + dailyMemberCertificationRepository.save(dailyMemberCertification); + dailyRoomCertificationRepository.save(dailyRoomCertification); + + // expected + mockMvc.perform(get("/rooms/my-join")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 참여 기록 조회 성공") + @WithMember(id = 1L) + @Test + void get_join_history_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); + Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); + Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); + + Participant participant1 = RoomFixture.participant(room1, 1L); + Participant participant2 = RoomFixture.participant(room2, 1L); + Participant participant3 = RoomFixture.participant(room3, 1L); + + roomRepository.saveAll(List.of(room1, room2, room3)); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + + participant3.removeRoom(); + participantRepository.flush(); + participantRepository.delete(participant3); + + // expected + mockMvc.perform(get("/rooms/join-history")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중이지 않은 방에 대한 확인 성공") + @WithMember + @Test + void check_if_participant_false_success() throws Exception { + // given + Room room = RoomFixture.room(); + Room savedRoom = roomRepository.save(room); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중이지 않은 방의 정보 불러오기 성공") + @Test + void get_un_joined_room_details() throws Exception { + // given + Room room = RoomFixture.room("테스트 방", NIGHT, 21); + Room savedRoom = roomRepository.save(room); + + Member member1 = MemberFixture.member("901010"); + member1 = memberRepository.save(member1); + + Item item = ItemFixture.nightMageSkin(); + + Inventory inventory = InventoryFixture.inventory(member1.getId(), item); + inventory.select(member1); + + itemRepository.save(item); + inventoryRepository.save(inventory); + + Participant participant = RoomFixture.participant(savedRoom, member1.getId()); + participantRepository.save(participant); + + Routine routine1 = RoomFixture.routine(savedRoom, "물 마시기"); + Routine routine2 = RoomFixture.routine(savedRoom, "커피 마시기"); + routineRepository.saveAll(List.of(routine1, routine2)); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member1.getId(), + savedRoom.getId(), participant); + dailyMemberCertificationRepository.save(dailyMemberCertification); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/un-joined")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중인 방에 대한 확인 성공") + @WithMember + @Test + void check_if_participant_true_success() throws Exception { + // given + Room room = RoomFixture.room(); + Room savedRoom = roomRepository.save(room); + + Participant participant = RoomFixture.participant(room, 1L); + participantRepository.save(participant); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침, 저녁 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 있음") + @WithMember(id = 1L) + @Test + void search_all_morning_night_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침, 저녁 방 전체 조회 성공 - 마지막 조회, 다음 페이지 없음") + @WithMember(id = 1L) + @Test + void search_last_page_all_morning_night_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms?roomId=5")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 없음") - @WithMember(id = 1L) - @Test - void search_last_page_all_morning_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms?roomId=5")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 없음") + @WithMember(id = 1L) + @Test + void search_last_page_all_morning_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms?roomType=MORNING")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드만 존재") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms?roomType=MORNING")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드만 존재") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=아침")) - .andExpect(status().isOk()) - .andDo(print()); - - mockMvc.perform(get("/rooms/search?keyword=방12")) - .andExpect(status().isOk()) - .andDo(print()); - - mockMvc.perform(get("/rooms/search?keyword=방")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 존재") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_roomType_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=아침")) + .andExpect(status().isOk()) + .andDo(print()); + + mockMvc.perform(get("/rooms/search?keyword=방12")) + .andExpect(status().isOk()) + .andDo(print()); + + mockMvc.perform(get("/rooms/search?keyword=방")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 존재") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_roomType_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=번째&roomType=MORNING")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 + 추가 페이지 존재X") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_roomType_roomId_success() throws Exception { - // given - Room room1 = RoomFixture.room("밤 - 첫 번째 방", RoomType.NIGHT, 1, "1234"); - Room room2 = RoomFixture.room("밤 - 두 번째 방", RoomType.NIGHT, 1); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("밤 - 아홉 번째 방", RoomType.NIGHT, 1, "5236"); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("밤 - 열둘 번째 방", RoomType.NIGHT, 1); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=루틴&roomType=NIGHT&roomId=3")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 수정전 정보 불러오기 성공") - @WithMember(id = 1L) - @Test - void get_room_details_before_modification_success() throws Exception { - // given - Member member2 = MemberFixture.member("123"); - Member member3 = MemberFixture.member("456"); - member2 = memberRepository.save(member2); - member3 = memberRepository.save(member3); - - Room room = RoomFixture.room("수정 전 방 제목", MORNING, 10, "1234"); - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - Participant participant2 = RoomFixture.participant(room, member2.getId()); - Participant participant3 = RoomFixture.participant(room, member3.getId()); - List routines = RoomFixture.routines(room); - - roomRepository.save(room); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - routineRepository.saveAll(routines); - - // expected - mockMvc.perform(get("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - } + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=번째&roomType=MORNING")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 + 추가 페이지 존재X") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_roomType_roomId_success() throws Exception { + // given + Room room1 = RoomFixture.room("밤 - 첫 번째 방", RoomType.NIGHT, 1, "1234"); + Room room2 = RoomFixture.room("밤 - 두 번째 방", RoomType.NIGHT, 1); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("밤 - 아홉 번째 방", RoomType.NIGHT, 1, "5236"); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("밤 - 열둘 번째 방", RoomType.NIGHT, 1); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=루틴&roomType=NIGHT&roomId=3")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 수정전 정보 불러오기 성공") + @WithMember(id = 1L) + @Test + void get_room_details_before_modification_success() throws Exception { + // given + Member member2 = MemberFixture.member("123"); + Member member3 = MemberFixture.member("456"); + member2 = memberRepository.save(member2); + member3 = memberRepository.save(member3); + + Room room = RoomFixture.room("수정 전 방 제목", MORNING, 10, "1234"); + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + Participant participant2 = RoomFixture.participant(room, member2.getId()); + Participant participant3 = RoomFixture.participant(room, member3.getId()); + List routines = RoomFixture.routines(room); + + roomRepository.save(room); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + routineRepository.saveAll(routines); + + // expected + mockMvc.perform(get("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + } } diff --git a/src/test/java/com/moabam/global/common/util/CookieMakeTest.java b/src/test/java/com/moabam/global/common/util/CookieMakeTest.java index 63cd7a79..5af52083 100644 --- a/src/test/java/com/moabam/global/common/util/CookieMakeTest.java +++ b/src/test/java/com/moabam/global/common/util/CookieMakeTest.java @@ -13,11 +13,13 @@ @ExtendWith(MockitoExtension.class) class CookieMakeTest { + String domain = "test"; + @DisplayName("prod환경에서 cookie 생성 테스트") @Test void create_test() { // Given - Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000); + Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000, domain); // When + Then assertAll( @@ -33,7 +35,7 @@ void create_test() { @Test void delete_test() { // given - Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000); + Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000, domain); // when Cookie deletedCookie = CookieUtils.deleteCookie(cookie); @@ -49,7 +51,7 @@ void delete_test() { @Test void typeCookie_create_test() { // Given + When - Cookie cookie = CookieUtils.typeCookie("Bearer", 10000); + Cookie cookie = CookieUtils.typeCookie("Bearer", 10000, domain); // then assertThat(cookie.getName()).isEqualTo("token_type"); diff --git a/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java index 31f6303a..24e0c5c4 100644 --- a/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java +++ b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java @@ -21,6 +21,7 @@ import com.moabam.api.application.auth.AuthorizationService; import com.moabam.api.application.auth.JwtAuthenticationService; import com.moabam.api.application.auth.JwtProviderService; +import com.moabam.api.domain.member.Role; import com.moabam.global.auth.filter.AuthorizationFilter; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.AuthorizationThreadLocal; @@ -98,7 +99,7 @@ void filter_have_any_access_token_error() throws ServletException, IOException { httpServletRequest.addHeader("token_type", "Bearer"); // when - String token = jwtProviderService.provideRefreshToken(); + String token = jwtProviderService.provideRefreshToken(Role.USER); httpServletRequest.setCookies(new Cookie("refresh_token", token)); authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); @@ -128,7 +129,7 @@ void filter_have_any_refresh_token_error() throws ServletException, IOException new Cookie("access_token", token)); when(jwtAuthenticationService.parseClaim(token)).thenReturn(publicClaim); - when(jwtAuthenticationService.isTokenExpire(token)).thenReturn(true); + when(jwtAuthenticationService.isTokenExpire(token, Role.USER)).thenReturn(true); authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); @@ -152,15 +153,15 @@ void issue_new_token_success() throws ServletException, IOException { // when String accessToken = jwtProviderService.provideAccessToken(publicClaim); - String refreshToken = jwtProviderService.provideRefreshToken(); + String refreshToken = jwtProviderService.provideRefreshToken(Role.USER); httpServletRequest.setCookies( new Cookie("token_type", "Bearer"), new Cookie("access_token", accessToken), new Cookie("refresh_token", refreshToken)); when(jwtAuthenticationService.parseClaim(accessToken)).thenReturn(publicClaim); - when(jwtAuthenticationService.isTokenExpire(accessToken)).thenReturn(true); - when(jwtAuthenticationService.isTokenExpire(refreshToken)).thenReturn(false); + when(jwtAuthenticationService.isTokenExpire(accessToken, Role.USER)).thenReturn(true); + when(jwtAuthenticationService.isTokenExpire(refreshToken, Role.USER)).thenReturn(false); authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); diff --git a/src/test/java/com/moabam/support/common/WithFilterSupporter.java b/src/test/java/com/moabam/support/common/WithFilterSupporter.java index 69b5f114..cd92c8cf 100644 --- a/src/test/java/com/moabam/support/common/WithFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithFilterSupporter.java @@ -13,6 +13,7 @@ import org.springframework.web.context.WebApplicationContext; import com.moabam.api.application.auth.JwtProviderService; +import com.moabam.api.domain.member.Role; import com.moabam.global.common.util.cookie.CookieUtils; import com.moabam.global.config.TokenConfig; import com.moabam.support.fixture.PublicClaimFixture; @@ -47,7 +48,7 @@ void setUpMockMvc(RestDocumentationContextProvider contextProvider) { jwtProviderService.provideAccessToken(PublicClaimFixture.publicClaim()), tokenConfig.getRefreshExpire())) .cookie(cookieUtils.tokenCookie("refresh_token", - jwtProviderService.provideRefreshToken(), + jwtProviderService.provideRefreshToken(Role.USER), tokenConfig.getRefreshExpire()))) .build(); } diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index 18d726cc..011c3514 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -1,10 +1,9 @@ package com.moabam.support.common; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; - -import java.util.Optional; - +import com.moabam.api.domain.member.Role; +import com.moabam.global.auth.filter.CorsFilter; +import com.moabam.global.auth.handler.PathResolver; +import com.moabam.global.config.AllowOriginConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.mock.mockito.MockBean; @@ -12,36 +11,36 @@ import org.springframework.context.annotation.Import; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import com.moabam.api.domain.member.Role; -import com.moabam.global.auth.filter.CorsFilter; -import com.moabam.global.auth.handler.PathResolver; -import com.moabam.global.config.AllowOriginConfig; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willReturn; @Import(DataCleanResolver.class) @ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) public class WithoutFilterSupporter { - @MockBean - private PathResolver pathResolver; + @MockBean + private PathResolver pathResolver; - @MockBean - private DefaultHandlerExceptionResolver handlerExceptionResolver; + @MockBean + private DefaultHandlerExceptionResolver handlerExceptionResolver; - @SpyBean - private CorsFilter corsFilter; + @SpyBean + private CorsFilter corsFilter; - @SpyBean - private AllowOriginConfig allowOriginConfig; + @MockBean + private AllowOriginConfig allowOriginConfig; - @BeforeEach - void setUpMock() { - willReturn("http://localhost:8080") - .given(corsFilter).secureMatch(any()); + @BeforeEach + void setUpMock() { + willReturn("http://localhost:8080") + .given(corsFilter).secureMatch(any()); - willReturn(Optional.of(PathResolver.Path.builder() - .uri("/") - .role(Role.USER) - .build())) - .given(pathResolver).permitPathMatch(any()); - } + willReturn(Optional.of(PathResolver.Path.builder() + .uri("/") + .role(Role.USER) + .build())) + .given(pathResolver).permitPathMatch(any()); + } } diff --git a/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java b/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java index 7a3c530a..50fa714c 100644 --- a/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java +++ b/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java @@ -7,12 +7,14 @@ public class JwtProviderFixture { public static final String originIss = "PARK"; public static final String originSecretKey = "testestestestestestestestestesttestestestestestestestestestest"; + public static final String adminKey = "testestestestestestestestestesttestestestestestestestestestest"; public static final long originId = 1L; public static final long originAccessExpire = 100000; public static final long originRefreshExpire = 150000; public static JwtProviderService jwtProviderService() { - TokenConfig tokenConfig = new TokenConfig(originIss, originAccessExpire, originRefreshExpire, originSecretKey); + TokenConfig tokenConfig = + new TokenConfig(originIss, originAccessExpire, originRefreshExpire, originSecretKey, adminKey); return new JwtProviderService(tokenConfig); } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index c485b58d..7083d3d7 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -70,8 +70,11 @@ token: access-expire: 100000 refresh-expire: 150000 secret-key: testestestestestestestestestesttestestestestestestestestestest + admin-secret: testestestestestestestestestesttestestestestestestestestestest allows: + admin-domain: "localhost" + domain: "localhost" origin: - "https://test.com" - "https://test.com" From ca1819d01f8d348d6d61d056bb55221a1fab7296 Mon Sep 17 00:00:00 2001 From: parksey Date: Sun, 3 Dec 2023 12:59:26 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=8B=9C=20=EB=B1=83=EC=A7=80=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/application/admin/AdminService.java | 3 + .../api/application/member/BadgeService.java | 32 + .../api/application/member/MemberMapper.java | 8 + .../room/CertificationService.java | 376 +- .../moabam/api/domain/member/BadgeType.java | 20 +- .../member/repository/BadgeRepository.java | 3 + .../room/CertificationServiceTest.java | 4 + .../domain/member/BadgeRepositoryTest.java | 84 + .../domain/member/MemberRepositoryTest.java | 9 +- .../presentation/MemberControllerTest.java | 46 +- .../api/presentation/RoomControllerTest.java | 3085 +++++++++-------- .../fixture/MemberInfoSearchFixture.java | 62 +- 12 files changed, 1949 insertions(+), 1783 deletions(-) create mode 100644 src/main/java/com/moabam/api/application/member/BadgeService.java create mode 100644 src/test/java/com/moabam/api/domain/member/BadgeRepositoryTest.java diff --git a/src/main/java/com/moabam/admin/application/admin/AdminService.java b/src/main/java/com/moabam/admin/application/admin/AdminService.java index fa46782b..01243bd7 100644 --- a/src/main/java/com/moabam/admin/application/admin/AdminService.java +++ b/src/main/java/com/moabam/admin/application/admin/AdminService.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.moabam.admin.domain.admin.Admin; import com.moabam.admin.domain.admin.AdminRepository; @@ -18,6 +19,7 @@ @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class AdminService { @Value("${admin}") @@ -31,6 +33,7 @@ public void validate(String state) { } } + @Transactional public LoginResponse signUpOrLogin(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { return login(authorizationTokenInfoResponse); } diff --git a/src/main/java/com/moabam/api/application/member/BadgeService.java b/src/main/java/com/moabam/api/application/member/BadgeService.java new file mode 100644 index 00000000..e89dc5dd --- /dev/null +++ b/src/main/java/com/moabam/api/application/member/BadgeService.java @@ -0,0 +1,32 @@ +package com.moabam.api.application.member; + +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.moabam.api.domain.member.Badge; +import com.moabam.api.domain.member.BadgeType; +import com.moabam.api.domain.member.repository.BadgeRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class BadgeService { + + private final BadgeRepository badgeRepository; + + public void createBadge(Long memberId, long certifyCount) { + Optional badgeType = BadgeType.getBadgeFrom(certifyCount); + + if (badgeType.isEmpty() + || badgeRepository.existsByMemberIdAndType(memberId, badgeType.get())) { + return; + } + + Badge badge = MemberMapper.toBadge(memberId, badgeType.get()); + badgeRepository.save(badge); + } +} diff --git a/src/main/java/com/moabam/api/application/member/MemberMapper.java b/src/main/java/com/moabam/api/application/member/MemberMapper.java index 67fd8a92..7d80c808 100644 --- a/src/main/java/com/moabam/api/application/member/MemberMapper.java +++ b/src/main/java/com/moabam/api/application/member/MemberMapper.java @@ -13,6 +13,7 @@ import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; import com.moabam.api.domain.item.ItemType; +import com.moabam.api.domain.member.Badge; import com.moabam.api.domain.member.BadgeType; import com.moabam.api.domain.member.Member; import com.moabam.api.dto.member.BadgeResponse; @@ -96,6 +97,13 @@ public static RankingInfo toRankingInfo(Member member) { .build(); } + public static Badge toBadge(Long memberId, BadgeType badgeType) { + return Badge.builder() + .type(badgeType) + .memberId(memberId) + .build(); + } + private static List badgedNames(Set badgeTypes) { return BadgeType.memberBadgeMap(badgeTypes); } diff --git a/src/main/java/com/moabam/api/application/room/CertificationService.java b/src/main/java/com/moabam/api/application/room/CertificationService.java index 82345f61..a6676f1a 100644 --- a/src/main/java/com/moabam/api/application/room/CertificationService.java +++ b/src/main/java/com/moabam/api/application/room/CertificationService.java @@ -1,200 +1,216 @@ package com.moabam.api.application.room; +import static com.moabam.global.common.util.GlobalConstant.*; +import static com.moabam.global.error.model.ErrorMessage.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import com.moabam.api.application.bug.BugService; +import com.moabam.api.application.member.BadgeService; import com.moabam.api.application.member.MemberService; import com.moabam.api.application.room.mapper.CertificationsMapper; import com.moabam.api.domain.bug.BugType; import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.room.*; -import com.moabam.api.domain.room.repository.*; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.DailyMemberCertification; +import com.moabam.api.domain.room.DailyRoomCertification; +import com.moabam.api.domain.room.Participant; +import com.moabam.api.domain.room.Room; +import com.moabam.api.domain.room.RoomExp; +import com.moabam.api.domain.room.Routine; +import com.moabam.api.domain.room.repository.CertificationRepository; +import com.moabam.api.domain.room.repository.CertificationsSearchRepository; +import com.moabam.api.domain.room.repository.DailyMemberCertificationRepository; +import com.moabam.api.domain.room.repository.DailyRoomCertificationRepository; +import com.moabam.api.domain.room.repository.ParticipantSearchRepository; +import com.moabam.api.domain.room.repository.RoutineRepository; import com.moabam.api.dto.room.CertifiedMemberInfo; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.common.util.UrlSubstringParser; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.NotFoundException; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import static com.moabam.global.common.util.GlobalConstant.MIDNIGHT_HOUR; -import static com.moabam.global.error.model.ErrorMessage.*; +import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class CertificationService { - private static final int REQUIRED_ROOM_CERTIFICATION = 75; - - private final RoutineRepository routineRepository; - private final CertificationRepository certificationRepository; - private final ParticipantSearchRepository participantSearchRepository; - private final CertificationsSearchRepository certificationsSearchRepository; - private final DailyRoomCertificationRepository dailyRoomCertificationRepository; - private final DailyMemberCertificationRepository dailyMemberCertificationRepository; - private final MemberService memberService; - private final BugService bugService; - private final ClockHolder clockHolder; - - @Transactional - public CertifiedMemberInfo getCertifiedMemberInfo(Long memberId, Long roomId, List imageUrls) { - LocalDate today = clockHolder.date(); - Participant participant = participantSearchRepository.findOne(memberId, roomId) - .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); - Room room = participant.getRoom(); - Member member = memberService.findMember(memberId); - BugType bugType = switch (room.getRoomType()) { - case MORNING -> BugType.MORNING; - case NIGHT -> BugType.NIGHT; - }; - - validateCertifyTime(clockHolder.times(), room.getCertifyTime()); - validateAlreadyCertified(memberId, roomId, today); - - certifyMember(memberId, roomId, participant, member, imageUrls); - - return CertificationsMapper.toCertifiedMemberInfo(today, bugType, room, member); - } - - @Transactional - public void certifyRoom(CertifiedMemberInfo certifyInfo) { - LocalDate date = certifyInfo.date(); - BugType bugType = certifyInfo.bugType(); - Room room = certifyInfo.room(); - Member member = certifyInfo.member(); - - Optional dailyRoomCertification = - certificationsSearchRepository.findDailyRoomCertification(room.getId(), date); - - if (dailyRoomCertification.isEmpty()) { - certifyRoomIfAvailable(room.getId(), date, room, bugType, room.getLevel()); - return; - } - - bugService.reward(member, bugType, room.getLevel()); - } - - public boolean existsMemberCertification(Long memberId, Long roomId, LocalDate date) { - return dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(memberId, roomId, - date.atStartOfDay(), date.atTime(LocalTime.MAX)); - } - - public boolean existsRoomCertification(Long roomId, LocalDate date) { - return dailyRoomCertificationRepository.existsByRoomIdAndCertifiedAt(roomId, date); - } - - public boolean existsAnyMemberCertification(Long roomId, LocalDate date) { - return dailyMemberCertificationRepository.existsByRoomIdAndCreatedAtBetween(roomId, date.atStartOfDay(), - date.atTime(LocalTime.MAX)); - } - - public Certification findCertification(Long certificationId) { - return certificationRepository.findById(certificationId) - .orElseThrow(() -> new NotFoundException(CERTIFICATION_NOT_FOUND)); - } - - public List findCertifications(List routines) { - return certificationsSearchRepository.findCertificationsByRoutines(routines); - } - - public void deleteCertifications(List certifications) { - certificationRepository.deleteAll(certifications); - } - - private void validateCertifyTime(LocalDateTime now, int certifyTime) { - LocalTime targetTime = LocalTime.of(certifyTime, 0); - LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); - - if (certifyTime == MIDNIGHT_HOUR && now.getHour() != MIDNIGHT_HOUR) { - targetDateTime = targetDateTime.plusDays(1); - } - - LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); - - if (now.isBefore(targetDateTime) || now.isAfter(plusTenMinutes)) { - throw new BadRequestException(INVALID_CERTIFY_TIME); - } - } - - private void validateAlreadyCertified(Long memberId, Long roomId, LocalDate today) { - if (certificationsSearchRepository.findDailyMemberCertification(memberId, roomId, today).isPresent()) { - throw new BadRequestException(DUPLICATED_DAILY_MEMBER_CERTIFICATION); - } - } - - private void certifyMember(Long memberId, Long roomId, Participant participant, Member member, List urls) { - DailyMemberCertification dailyMemberCertification = CertificationsMapper.toDailyMemberCertification(memberId, - roomId, participant); - dailyMemberCertificationRepository.save(dailyMemberCertification); - member.increaseTotalCertifyCount(); - participant.updateCertifyCount(); - - saveNewCertifications(memberId, urls); - } - - private void saveNewCertifications(Long memberId, List imageUrls) { - List certifications = new ArrayList<>(); - - for (String imageUrl : imageUrls) { - Long routineId = Long.parseLong(UrlSubstringParser.parseUrl(imageUrl, "_")); - Routine routine = routineRepository.findById(routineId).orElseThrow(() -> new NotFoundException( - ROUTINE_NOT_FOUND)); - - Certification certification = CertificationsMapper.toCertification(routine, memberId, imageUrl); - certifications.add(certification); - } - - certificationRepository.saveAll(certifications); - } - - private void certifyRoomIfAvailable(Long roomId, LocalDate today, Room room, BugType bugType, int roomLevel) { - List dailyMemberCertifications = - certificationsSearchRepository.findSortedDailyMemberCertifications(roomId, today); - double completePercentage = calculateCompletePercentage(dailyMemberCertifications.size(), - room.getCurrentUserCount()); - - if (completePercentage >= REQUIRED_ROOM_CERTIFICATION) { - DailyRoomCertification createDailyRoomCertification = CertificationsMapper.toDailyRoomCertification( - roomId, today); - - dailyRoomCertificationRepository.save(createDailyRoomCertification); - int expAppliedRoomLevel = getRoomLevelAfterExpApply(roomLevel, room); - - provideBugToCompletedMembers(bugType, dailyMemberCertifications, expAppliedRoomLevel); - } - } - - private double calculateCompletePercentage(int certifiedMembersCount, int currentsMembersCount) { - double completePercentage = ((double) certifiedMembersCount / currentsMembersCount) * 100; - - return Math.round(completePercentage * 100) / 100.0; - } - - private int getRoomLevelAfterExpApply(int roomLevel, Room room) { - int requireExp = RoomExp.of(roomLevel).getTotalExp(); - room.gainExp(); - - if (room.getExp() == requireExp) { - room.levelUp(); - } - - return room.getLevel(); - } - - private void provideBugToCompletedMembers(BugType bugType, List dailyMemberCertifications, - int expAppliedRoomLevel) { - List memberIds = dailyMemberCertifications.stream() - .map(DailyMemberCertification::getMemberId) - .toList(); - - memberService.getRoomMembers(memberIds) - .forEach(completedMember -> bugService.reward(completedMember, bugType, expAppliedRoomLevel)); - } + private static final int REQUIRED_ROOM_CERTIFICATION = 75; + + private final RoutineRepository routineRepository; + private final CertificationRepository certificationRepository; + private final ParticipantSearchRepository participantSearchRepository; + private final CertificationsSearchRepository certificationsSearchRepository; + private final DailyRoomCertificationRepository dailyRoomCertificationRepository; + private final DailyMemberCertificationRepository dailyMemberCertificationRepository; + private final MemberService memberService; + private final BadgeService badgeService; + private final BugService bugService; + private final ClockHolder clockHolder; + + @Transactional + public CertifiedMemberInfo getCertifiedMemberInfo(Long memberId, Long roomId, List imageUrls) { + LocalDate today = clockHolder.date(); + Participant participant = participantSearchRepository.findOne(memberId, roomId) + .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); + Room room = participant.getRoom(); + Member member = memberService.findMember(memberId); + BugType bugType = switch (room.getRoomType()) { + case MORNING -> BugType.MORNING; + case NIGHT -> BugType.NIGHT; + }; + + validateCertifyTime(clockHolder.times(), room.getCertifyTime()); + validateAlreadyCertified(memberId, roomId, today); + + certifyMember(memberId, roomId, participant, member, imageUrls); + + return CertificationsMapper.toCertifiedMemberInfo(today, bugType, room, member); + } + + @Transactional + public void certifyRoom(CertifiedMemberInfo certifyInfo) { + LocalDate date = certifyInfo.date(); + BugType bugType = certifyInfo.bugType(); + Room room = certifyInfo.room(); + Member member = certifyInfo.member(); + + Optional dailyRoomCertification = + certificationsSearchRepository.findDailyRoomCertification(room.getId(), date); + + if (dailyRoomCertification.isEmpty()) { + certifyRoomIfAvailable(room.getId(), date, room, bugType, room.getLevel()); + return; + } + + bugService.reward(member, bugType, room.getLevel()); + } + + public boolean existsMemberCertification(Long memberId, Long roomId, LocalDate date) { + return dailyMemberCertificationRepository.existsByMemberIdAndRoomIdAndCreatedAtBetween(memberId, roomId, + date.atStartOfDay(), date.atTime(LocalTime.MAX)); + } + + public boolean existsRoomCertification(Long roomId, LocalDate date) { + return dailyRoomCertificationRepository.existsByRoomIdAndCertifiedAt(roomId, date); + } + + public boolean existsAnyMemberCertification(Long roomId, LocalDate date) { + return dailyMemberCertificationRepository.existsByRoomIdAndCreatedAtBetween(roomId, date.atStartOfDay(), + date.atTime(LocalTime.MAX)); + } + + public Certification findCertification(Long certificationId) { + return certificationRepository.findById(certificationId) + .orElseThrow(() -> new NotFoundException(CERTIFICATION_NOT_FOUND)); + } + + public List findCertifications(List routines) { + return certificationsSearchRepository.findCertificationsByRoutines(routines); + } + + public void deleteCertifications(List certifications) { + certificationRepository.deleteAll(certifications); + } + + private void validateCertifyTime(LocalDateTime now, int certifyTime) { + LocalTime targetTime = LocalTime.of(certifyTime, 0); + LocalDateTime targetDateTime = LocalDateTime.of(now.toLocalDate(), targetTime); + + if (certifyTime == MIDNIGHT_HOUR && now.getHour() != MIDNIGHT_HOUR) { + targetDateTime = targetDateTime.plusDays(1); + } + + LocalDateTime plusTenMinutes = targetDateTime.plusMinutes(10); + + if (now.isBefore(targetDateTime) || now.isAfter(plusTenMinutes)) { + throw new BadRequestException(INVALID_CERTIFY_TIME); + } + } + + private void validateAlreadyCertified(Long memberId, Long roomId, LocalDate today) { + if (certificationsSearchRepository.findDailyMemberCertification(memberId, roomId, today).isPresent()) { + throw new BadRequestException(DUPLICATED_DAILY_MEMBER_CERTIFICATION); + } + } + + private void certifyMember(Long memberId, Long roomId, Participant participant, Member member, List urls) { + DailyMemberCertification dailyMemberCertification = CertificationsMapper.toDailyMemberCertification(memberId, + roomId, participant); + dailyMemberCertificationRepository.save(dailyMemberCertification); + member.increaseTotalCertifyCount(); + badgeService.createBadge(member.getId(), member.getTotalCertifyCount()); + participant.updateCertifyCount(); + + saveNewCertifications(memberId, urls); + } + + private void saveNewCertifications(Long memberId, List imageUrls) { + List certifications = new ArrayList<>(); + + for (String imageUrl : imageUrls) { + Long routineId = Long.parseLong(UrlSubstringParser.parseUrl(imageUrl, "_")); + Routine routine = routineRepository.findById(routineId).orElseThrow(() -> new NotFoundException( + ROUTINE_NOT_FOUND)); + + Certification certification = CertificationsMapper.toCertification(routine, memberId, imageUrl); + certifications.add(certification); + } + + certificationRepository.saveAll(certifications); + } + + private void certifyRoomIfAvailable(Long roomId, LocalDate today, Room room, BugType bugType, int roomLevel) { + List dailyMemberCertifications = + certificationsSearchRepository.findSortedDailyMemberCertifications(roomId, today); + double completePercentage = calculateCompletePercentage(dailyMemberCertifications.size(), + room.getCurrentUserCount()); + + if (completePercentage >= REQUIRED_ROOM_CERTIFICATION) { + DailyRoomCertification createDailyRoomCertification = CertificationsMapper.toDailyRoomCertification( + roomId, today); + + dailyRoomCertificationRepository.save(createDailyRoomCertification); + int expAppliedRoomLevel = getRoomLevelAfterExpApply(roomLevel, room); + + provideBugToCompletedMembers(bugType, dailyMemberCertifications, expAppliedRoomLevel); + } + } + + private double calculateCompletePercentage(int certifiedMembersCount, int currentsMembersCount) { + double completePercentage = ((double)certifiedMembersCount / currentsMembersCount) * 100; + + return Math.round(completePercentage * 100) / 100.0; + } + + private int getRoomLevelAfterExpApply(int roomLevel, Room room) { + int requireExp = RoomExp.of(roomLevel).getTotalExp(); + room.gainExp(); + + if (room.getExp() == requireExp) { + room.levelUp(); + } + + return room.getLevel(); + } + + private void provideBugToCompletedMembers(BugType bugType, List dailyMemberCertifications, + int expAppliedRoomLevel) { + List memberIds = dailyMemberCertifications.stream() + .map(DailyMemberCertification::getMemberId) + .toList(); + + memberService.getRoomMembers(memberIds) + .forEach(completedMember -> bugService.reward(completedMember, bugType, expAppliedRoomLevel)); + } } diff --git a/src/main/java/com/moabam/api/domain/member/BadgeType.java b/src/main/java/com/moabam/api/domain/member/BadgeType.java index 82a7ebd1..e0819f9a 100644 --- a/src/main/java/com/moabam/api/domain/member/BadgeType.java +++ b/src/main/java/com/moabam/api/domain/member/BadgeType.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Set; import com.moabam.api.dto.member.BadgeResponse; @@ -11,16 +12,15 @@ @Getter public enum BadgeType { - MORNING_BIRTH("MORNING", "오목눈이 탄생"), - MORNING_ADULT("MORNING", "어른 오목눈이"), - NIGHT_BIRTH("NIGHT", "부엉이 탄생"), - NIGHT_ADULT("NIGHT", "어른 부엉이"); + BIRTH(10, "탄생 축하 뱃지"), + LEVEL10(100, "10레벨 뱃지"), + LEVEL50(500, "50레벨 뱃지"); - private final String period; + private final long certifyCount; private final String korean; - BadgeType(String period, String korean) { - this.period = period; + BadgeType(long certifyCount, String korean) { + this.certifyCount = certifyCount; this.korean = korean; } @@ -32,4 +32,10 @@ public static List memberBadgeMap(Set badgeTypes) { .build()) .toList(); } + + public static Optional getBadgeFrom(long certifyCount) { + return Arrays.stream(BadgeType.values()) + .filter(badgeType -> badgeType.certifyCount == certifyCount) + .findFirst(); + } } diff --git a/src/main/java/com/moabam/api/domain/member/repository/BadgeRepository.java b/src/main/java/com/moabam/api/domain/member/repository/BadgeRepository.java index dd16ebff..ac313e25 100644 --- a/src/main/java/com/moabam/api/domain/member/repository/BadgeRepository.java +++ b/src/main/java/com/moabam/api/domain/member/repository/BadgeRepository.java @@ -3,7 +3,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import com.moabam.api.domain.member.Badge; +import com.moabam.api.domain.member.BadgeType; public interface BadgeRepository extends JpaRepository { + boolean existsByMemberIdAndType(Long memberId, BadgeType type); + } diff --git a/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java b/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java index f35b1483..e02eac44 100644 --- a/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java +++ b/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java @@ -21,6 +21,7 @@ import com.moabam.api.application.bug.BugService; import com.moabam.api.application.image.ImageService; +import com.moabam.api.application.member.BadgeService; import com.moabam.api.application.member.MemberService; import com.moabam.api.application.room.mapper.CertificationsMapper; import com.moabam.api.domain.bug.BugType; @@ -82,6 +83,9 @@ class CertificationServiceTest { @Mock private ImageService imageService; + @Mock + private BadgeService badgeService; + @Mock private ClockHolder clockHolder; diff --git a/src/test/java/com/moabam/api/domain/member/BadgeRepositoryTest.java b/src/test/java/com/moabam/api/domain/member/BadgeRepositoryTest.java new file mode 100644 index 00000000..a250f84b --- /dev/null +++ b/src/test/java/com/moabam/api/domain/member/BadgeRepositoryTest.java @@ -0,0 +1,84 @@ +package com.moabam.api.domain.member; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; + +import com.moabam.api.application.member.BadgeService; +import com.moabam.api.domain.member.repository.BadgeRepository; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.support.annotation.QuerydslRepositoryTest; +import com.moabam.support.fixture.MemberFixture; + +@QuerydslRepositoryTest +class BadgeRepositoryTest { + + @Autowired + BadgeRepository badgeRepository; + + @Autowired + MemberRepository memberRepository; + + @DisplayName("인증 횟수에 따른 값 뱃지 확인") + @Test + void get_badge_by_certifyCount() { + assertThat(BadgeType.getBadgeFrom(10).get()).isEqualTo(BadgeType.BIRTH); + assertThat(BadgeType.getBadgeFrom(100).get()).isEqualTo(BadgeType.LEVEL10); + assertThat(BadgeType.getBadgeFrom(500).get()).isEqualTo(BadgeType.LEVEL50); + assertThat(BadgeType.getBadgeFrom(9)).isEmpty(); + } + + @DisplayName("뱃지 생성 성공") + @ParameterizedTest + @ValueSource(ints = {10, 100, 500}) + void member_get_badge_success(int certifyCount) { + // given + BadgeService badgeService = new BadgeService(badgeRepository); + + Member member = MemberFixture.member(); + for (int i = 0; i < certifyCount; i++) { + member.increaseTotalCertifyCount(); + } + + memberRepository.save(member); + + // when + badgeService.createBadge(member.getId(), member.getTotalCertifyCount()); + BadgeType expectedType = BadgeType.getBadgeFrom(certifyCount).get(); + + // then + assertThat(badgeRepository.existsByMemberIdAndType(member.getId(), expectedType)) + .isTrue(); + } + + @DisplayName("뱃지가 있으면 저장하지 않는다.") + @ParameterizedTest + @ValueSource(ints = {10, 100, 500}) + void already_exist_bage_then_no_save(int certifyCount) { + // given + BadgeService badgeService = new BadgeService(badgeRepository); + + Member member = MemberFixture.member(); + for (int i = 0; i < certifyCount; i++) { + member.increaseTotalCertifyCount(); + } + + memberRepository.save(member); + + // when + BadgeType expectedType = BadgeType.getBadgeFrom(certifyCount).get(); + + Badge badge = Badge.builder().memberId(member.getId()).type(expectedType).build(); + badgeRepository.save(badge); + + // then + assertThatNoException() + .isThrownBy(() -> badgeService.createBadge(member.getId(), member.getTotalCertifyCount())); + assertThat(badgeRepository.existsByMemberIdAndType(member.getId(), expectedType)) + .isTrue(); + } +} diff --git a/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java b/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java index 90729351..ee18e08c 100644 --- a/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java +++ b/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java @@ -137,11 +137,10 @@ void search_info_success() { member.enterRoom(RoomType.MORNING); memberRepository.save(member); - Badge morningBirth = BadgeFixture.badge(member.getId(), BadgeType.MORNING_BIRTH); - Badge morningAdult = BadgeFixture.badge(member.getId(), BadgeType.MORNING_ADULT); - Badge nightBirth = BadgeFixture.badge(member.getId(), BadgeType.NIGHT_BIRTH); - Badge nightAdult = BadgeFixture.badge(member.getId(), BadgeType.NIGHT_ADULT); - List badges = List.of(morningBirth, morningAdult, nightBirth, nightAdult); + Badge birth = BadgeFixture.badge(member.getId(), BadgeType.BIRTH); + Badge level50 = BadgeFixture.badge(member.getId(), BadgeType.LEVEL50); + Badge level10 = BadgeFixture.badge(member.getId(), BadgeType.LEVEL10); + List badges = List.of(birth, level10, level50); badgeRepository.saveAll(badges); // when diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index e07a6ff8..80efe377 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -265,10 +265,10 @@ void unlink_social_member_failby_meber_is_manger() throws Exception { @Test void search_my_info_success() throws Exception { // given - Badge morningBirth = BadgeFixture.badge(member.getId(), BadgeType.MORNING_BIRTH); - Badge morningAdult = BadgeFixture.badge(member.getId(), BadgeType.MORNING_ADULT); - Badge nightBirth = BadgeFixture.badge(member.getId(), BadgeType.NIGHT_BIRTH); - List badges = List.of(morningBirth, morningAdult, nightBirth); + Badge birth = BadgeFixture.badge(member.getId(), BadgeType.BIRTH); + Badge level50 = BadgeFixture.badge(member.getId(), BadgeType.LEVEL50); + Badge level10 = BadgeFixture.badge(member.getId(), BadgeType.LEVEL10); + List badges = List.of(birth, level10, level50); badgeRepository.saveAll(badges); Item night = ItemFixture.nightMageSkin(); @@ -302,14 +302,12 @@ void search_my_info_success() throws Exception { // MockMvcResultMatchers.jsonPath("$.birds.MORNING").value(morningInven.getItem().getImage()), // MockMvcResultMatchers.jsonPath("$.birds.NIGHT").value(nightInven.getItem().getImage()), - MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("오목눈이 탄생"), + MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("탄생 축하 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[0].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("어른 오목눈이"), + MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("10레벨 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[1].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("부엉이 탄생"), + MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("50레벨 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[2].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[3].badge").value("어른 부엉이"), - MockMvcResultMatchers.jsonPath("$.badges[3].unlock").value(false), MockMvcResultMatchers.jsonPath("$.goldenBug").value(member.getBug().getGoldenBug()), MockMvcResultMatchers.jsonPath("$.morningBug").value(member.getBug().getMorningBug()), MockMvcResultMatchers.jsonPath("$.nightBug").value(member.getBug().getNightBug()) @@ -353,14 +351,12 @@ void search_my_info_with_no_badge_success() throws Exception { // MockMvcResultMatchers.jsonPath("$.birds.MORNING").value(morningInven.getItem().getImage()), // MockMvcResultMatchers.jsonPath("$.birds.NIGHT").value(nightInven.getItem().getImage()), - MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("오목눈이 탄생"), + MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("탄생 축하 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[0].unlock").value(false), - MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("어른 오목눈이"), + MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("10레벨 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[1].unlock").value(false), - MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("부엉이 탄생"), + MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("50레벨 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[2].unlock").value(false), - MockMvcResultMatchers.jsonPath("$.badges[3].badge").value("어른 부엉이"), - MockMvcResultMatchers.jsonPath("$.badges[3].unlock").value(false), MockMvcResultMatchers.jsonPath("$.goldenBug").value(member.getBug().getGoldenBug()), MockMvcResultMatchers.jsonPath("$.morningBug").value(member.getBug().getMorningBug()), MockMvcResultMatchers.jsonPath("$.nightBug").value(member.getBug().getNightBug()) @@ -375,11 +371,9 @@ void search_friend_info_success() throws Exception { Member friend = MemberFixture.member("123456789"); memberRepository.save(friend); - Badge morningBirth = BadgeFixture.badge(friend.getId(), BadgeType.MORNING_BIRTH); - Badge morningAdult = BadgeFixture.badge(friend.getId(), BadgeType.MORNING_ADULT); - Badge nightBirth = BadgeFixture.badge(friend.getId(), BadgeType.NIGHT_BIRTH); - Badge nightAdult = BadgeFixture.badge(friend.getId(), BadgeType.NIGHT_ADULT); - List badges = List.of(morningBirth, morningAdult, nightBirth, nightAdult); + Badge birth = BadgeFixture.badge(friend.getId(), BadgeType.BIRTH); + Badge level10 = BadgeFixture.badge(friend.getId(), BadgeType.LEVEL10); + List badges = List.of(birth, level10); badgeRepository.saveAll(badges); Item night = ItemFixture.nightMageSkin(); @@ -388,10 +382,10 @@ void search_friend_info_success() throws Exception { itemRepository.saveAll(List.of(night, morning, killer)); Inventory nightInven = InventoryFixture.inventory(friend.getId(), night); - nightInven.select(member); + nightInven.select(friend); Inventory morningInven = InventoryFixture.inventory(friend.getId(), morning); - morningInven.select(member); + morningInven.select(friend); Inventory killerInven = InventoryFixture.inventory(friend.getId(), killer); friend.changeDefaultSkintUrl(morning); @@ -416,14 +410,12 @@ void search_friend_info_success() throws Exception { MockMvcResultMatchers.jsonPath("$.birds.MORNING").value(morningInven.getItem().getAwakeImage()), MockMvcResultMatchers.jsonPath("$.birds.NIGHT").value(nightInven.getItem().getAwakeImage()), - MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("오목눈이 탄생"), + MockMvcResultMatchers.jsonPath("$.badges[0].badge").value("탄생 축하 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[0].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("어른 오목눈이"), + MockMvcResultMatchers.jsonPath("$.badges[1].badge").value("10레벨 뱃지"), MockMvcResultMatchers.jsonPath("$.badges[1].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("부엉이 탄생"), - MockMvcResultMatchers.jsonPath("$.badges[2].unlock").value(true), - MockMvcResultMatchers.jsonPath("$.badges[3].badge").value("어른 부엉이"), - MockMvcResultMatchers.jsonPath("$.badges[3].unlock").value(true) + MockMvcResultMatchers.jsonPath("$.badges[2].badge").value("50레벨 뱃지"), + MockMvcResultMatchers.jsonPath("$.badges[2].unlock").value(false) ).andDo(print()); } diff --git a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java index 613838d2..d04794d1 100644 --- a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java @@ -1,5 +1,32 @@ package com.moabam.api.presentation; +import static com.moabam.api.domain.room.RoomType.*; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.http.MediaType.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.BDDMockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; @@ -7,39 +34,31 @@ import com.moabam.api.domain.item.repository.ItemRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.domain.room.*; -import com.moabam.api.domain.room.repository.*; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.DailyMemberCertification; +import com.moabam.api.domain.room.DailyRoomCertification; +import com.moabam.api.domain.room.Participant; +import com.moabam.api.domain.room.Room; +import com.moabam.api.domain.room.RoomType; +import com.moabam.api.domain.room.Routine; +import com.moabam.api.domain.room.repository.CertificationRepository; +import com.moabam.api.domain.room.repository.DailyMemberCertificationRepository; +import com.moabam.api.domain.room.repository.DailyRoomCertificationRepository; +import com.moabam.api.domain.room.repository.ParticipantRepository; +import com.moabam.api.domain.room.repository.ParticipantSearchRepository; +import com.moabam.api.domain.room.repository.RoomRepository; +import com.moabam.api.domain.room.repository.RoutineRepository; import com.moabam.api.dto.room.CreateRoomRequest; import com.moabam.api.dto.room.EnterRoomRequest; import com.moabam.api.dto.room.ModifyRoomRequest; import com.moabam.global.common.util.SystemClockHolder; import com.moabam.support.annotation.WithMember; import com.moabam.support.common.WithoutFilterSupporter; -import com.moabam.support.fixture.*; -import org.junit.jupiter.api.*; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.mockito.BDDMockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -import static com.moabam.api.domain.room.RoomType.MORNING; -import static com.moabam.api.domain.room.RoomType.NIGHT; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.moabam.support.fixture.BugFixture; +import com.moabam.support.fixture.InventoryFixture; +import com.moabam.support.fixture.ItemFixture; +import com.moabam.support.fixture.MemberFixture; +import com.moabam.support.fixture.RoomFixture; @Transactional @SpringBootTest @@ -47,1535 +66,1535 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class RoomControllerTest extends WithoutFilterSupporter { - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private RoomRepository roomRepository; - - @Autowired - private RoutineRepository routineRepository; - - @Autowired - private ParticipantRepository participantRepository; - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private CertificationRepository certificationRepository; - - @Autowired - private DailyMemberCertificationRepository dailyMemberCertificationRepository; - - @Autowired - private DailyRoomCertificationRepository dailyRoomCertificationRepository; - - @Autowired - private ParticipantSearchRepository participantSearchRepository; - - @Autowired - private ItemRepository itemRepository; - - @Autowired - private InventoryRepository inventoryRepository; - - @SpyBean - private SystemClockHolder clockHolder; - - Member member; - - @BeforeAll - void setUp() { - member = MemberFixture.member(); - memberRepository.save(member); - } - - @AfterEach - void cleanUp() { - while (member.getCurrentMorningCount() > 0) { - member.exitRoom(MORNING); - } - - while (member.getCurrentNightCount() > 0) { - member.exitRoom(NIGHT); - } - } - - @DisplayName("비밀번호 없는 방 생성 성공") - @WithMember - @Test - void create_room_no_password_success() throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isCreated()) - .andDo(print()); - - assertThat(roomRepository.findAll()).hasSize(1); - assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("재윤과 앵맹이의 방임"); - assertThat(roomRepository.findAll().get(0).getPassword()).isNull(); - } - - @DisplayName("비밀번호 있는 방 생성 성공") - @WithMember - @ParameterizedTest - @CsvSource({ - "1234", "12345678", "98765" - }) - void create_room_with_password_success(String password) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 있는 재맹의 방임", password, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isCreated()) - .andDo(print()); - - assertThat(roomRepository.findAll()).hasSize(1); - assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("비번 있는 재맹의 방임"); - assertThat(roomRepository.findAll().get(0).getPassword()).isEqualTo(password); - } - - @DisplayName("올바르지 않은 비밀번호 방 생성시 예외 발생") - @ParameterizedTest - @CsvSource({ - "1", "12", "123", "123456789", "abc" - }) - void create_room_with_wrong_password_fail(String password) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 있는 재윤과 앵맹이의 방임", password, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("Routine 갯수를 초과한 방 생성시 예외 발생") - @Test - void create_room_with_too_many_routine_fail() throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - routines.add("밥 먹기"); - routines.add("코드 리뷰 달기"); - routines.add("책 읽기"); - routines.add("산책 하기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("Routine 없는 방 생성시 예외 발생") - @Test - void create_room_with_no_routine_fail() throws Exception { - // given - List routines = new ArrayList<>(); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("올바르지 못한 시간으로 아침 방 생성시 예외 발생") - @ParameterizedTest - @CsvSource({ - "1", "3", "11", "12", "20" - }) - void create_morning_room_wrong_certify_time_fail(int certifyTime) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, certifyTime, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("올바르지 못한 시간으로 저녁 방 생성시 에외 발생") - @ParameterizedTest - @CsvSource({ - "19", "3", "6", "9" - }) - void create_night_room_wrong_certify_time_fail(int certifyTime) throws Exception { - // given - List routines = new ArrayList<>(); - routines.add("물 마시기"); - routines.add("코테 풀기"); - - CreateRoomRequest createRoomRequest = new CreateRoomRequest( - "비번 없는 재윤과 앵맹이의 방임", null, routines, NIGHT, certifyTime, 4); - String json = objectMapper.writeValueAsString(createRoomRequest); - - // expected - mockMvc.perform(post("/rooms") - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("방 수정 성공 - 방장일 경우") - @WithMember(id = 1L) - @Test - void modify_room_success() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - List routines = RoomFixture.routines(room); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - roomRepository.save(room); - routineRepository.saveAll(routines); - participantRepository.save(participant); - - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "공지공지", "4567", 10, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - - Room modifiedRoom = roomRepository.findById(room.getId()).orElseThrow(); - List modifiedRoutines = routineRepository.findAllByRoomId(room.getId()); - - assertThat(modifiedRoom.getTitle()).isEqualTo("수정할 방임!"); - assertThat(modifiedRoom.getCertifyTime()).isEqualTo(10); - assertThat(modifiedRoom.getPassword()).isEqualTo("4567"); - assertThat(modifiedRoom.getAnnouncement()).isEqualTo("공지공지"); - assertThat(modifiedRoom.getMaxUserCount()).isEqualTo(7); - assertThat(modifiedRoutines).hasSize(2); - } - - @DisplayName("방 수정 실패 - 방장 아닐 경우") - @WithMember(id = 1L) - @Test - void unauthorized_modify_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - roomRepository.save(room); - participantRepository.save(participant); - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 9, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - String message = "{\"message\":\"방장이 아닌 사용자는 방을 수정할 수 없습니다.\"}"; - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isNotFound()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("방 수정 실패 - 이미 한 참여자가 인증하고 방의 인증 시간을 바꾸려고 할때 예외 처리") - @WithMember(id = 1L) - @Test - void room_certify_time_modification_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - room = roomRepository.save(room); - - Member member2 = MemberFixture.member("12313123"); - member2 = memberRepository.save(member2); - - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - - Participant participant2 = RoomFixture.participant(room, member2.getId()); - - participantRepository.saveAll(List.of(participant1, participant2)); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member2.getId(), - room.getId(), participant2); - - dailyMemberCertificationRepository.save(dailyMemberCertification); - - ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 10, 7); - String json = objectMapper.writeValueAsString(modifyRoomRequest); - - // expected - mockMvc.perform(put("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("비밀번호 있는 방 참여 성공") - @WithMember(id = 1L) - @Test - void enter_room_with_password_success() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("비밀번호 없는 방 참여 성공") - @WithMember(id = 1L) - @Test - void enter_room_with_no_password_success() throws Exception { - // given - BDDMockito.given(clockHolder.times()).willReturn(LocalDateTime.of(2023, 12, 3, 14, 30, 0)); - Room room = RoomFixture.room(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest(null); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 참여 후 인원수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_room_user_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); - - // then - assertThat(findRoom.getCurrentUserCount()).isEqualTo(2); - } - - @DisplayName("아침 방 참여 후 사용자의 방 입장 횟수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_morning_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentMorningCount()).isEqualTo(1); - assertThat(getMember.getCurrentNightCount()).isZero(); - } - - @DisplayName("저녁 방 참여 후 사용자의 방 입장 횟수 증가 테스트") - @WithMember(id = 1L) - @Test - void enter_and_increase_night_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(5) - .build(); - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentNightCount()).isEqualTo(1); - assertThat(getMember.getCurrentMorningCount()).isZero(); - } - - @DisplayName("사용자의 아침 방 입장 횟수 3일시 예외 처리") - @Test - void enter_and_morning_room_over_three_fail() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 3; i++) { - member.enterRoom(MORNING); - } - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()); - } - - @DisplayName("사용자의 저녁 방 입장 횟수 3일시 예외 처리") - @Test - void enter_and_night_room_over_three_fail() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(22) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 3; i++) { - member.enterRoom(NIGHT); - } - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - - // when - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()); - } - - @DisplayName("비밀번호 불일치 방 참여시 예외 발생") - @WithMember(id = 1L) - @Test - void enter_room_wrong_password_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - Member member = Member.builder() - .id(1L) - .socialId("1") - .bug(BugFixture.bug()) - .build(); - - memberRepository.save(member); - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - String message = "{\"message\":\"방의 비밀번호가 일치하지 않습니다.\"}"; - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("인원수가 모두 찬 방 참여시 예외 발생") - @WithMember(id = 1L) - @Test - void enter_max_user_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("처음 제목") - .password("7777") - .roomType(MORNING) - .certifyTime(9) - .maxUserCount(5) - .build(); - - for (int i = 0; i < 4; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); - String json = objectMapper.writeValueAsString(enterRoomRequest); - String message = "{\"message\":\"방의 인원수가 찼습니다.\"}"; - - // expected - mockMvc.perform(post("/rooms/" + room.getId()) - .contentType(APPLICATION_JSON) - .content(json)) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("일반 사용자의 방 나가기 성공") - @WithMember(id = 1L) - @Test - void no_manager_exit_room_success() throws Exception { - // given - Room room = Room.builder() - .title("5명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(8) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 4; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - participantRepository.save(participant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - - Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); - List deletedParticipant = participantRepository.findAll(); - - assertThat(findRoom.getCurrentUserCount()).isEqualTo(4); - assertThat(deletedParticipant).hasSize(1); - assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); - assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); - } - - @DisplayName("방장의 방 나가기 - 방 삭제 성공") - @WithMember(id = 1L) - @Test - void manager_delete_room_success() throws Exception { - // given - Room room = Room.builder() - .title("1명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(8) - .build(); - - List routines = RoomFixture.routines(room); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - roomRepository.save(room); - routineRepository.saveAll(routines); - participantRepository.save(participant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - - List deletedRoom = roomRepository.findAll(); - List deletedRoutine = routineRepository.findAll(); - List deletedParticipant = participantRepository.findAll(); - - assertThat(deletedRoom).isEmpty(); - assertThat(deletedRoutine).hasSize(0); - assertThat(deletedParticipant).hasSize(1); - assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); - assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); - } - - @DisplayName("방장이 위임하지 않고 방 나가기 실패") - @WithMember(id = 1L) - @Test - void manager_exit_room_fail() throws Exception { - // given - Room room = Room.builder() - .title("7명이 있는 방~") - .roomType(NIGHT) - .certifyTime(21) - .maxUserCount(10) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - participant.enableManager(); - - for (int i = 0; i < 6; i++) { - room.increaseCurrentUserCount(); - } - - roomRepository.save(room); - participantRepository.save(participant); - String message = "{\"message\":\"인원수가 2명 이상일 때는 방장을 위임해야 합니다.\"}"; - - // expected - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isBadRequest()) - .andExpect(content().json(message)) - .andDo(print()); - } - - @DisplayName("아침 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") - @WithMember(id = 1L) - @Test - void exit_and_decrease_morning_room_count() throws Exception { - // given - Room room = RoomFixture.room(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 3; i++) { - member.enterRoom(RoomType.MORNING); - } - - memberRepository.save(member); - roomRepository.save(room); - participantRepository.save(participant); - - // when - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentMorningCount()).isEqualTo(2); - } - - @DisplayName("저녁 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") - @WithMember(id = 1L) - @Test - void exit_and_decrease_night_room_count() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(23) - .maxUserCount(5) - .build(); - - Participant participant = RoomFixture.participant(room, 1L); - - for (int i = 0; i < 3; i++) { - member.enterRoom(NIGHT); - } - - memberRepository.save(member); - roomRepository.save(room); - participantRepository.save(participant); - - // when - mockMvc.perform(delete("/rooms/" + room.getId())) - .andExpect(status().isOk()); - - Member getMember = memberRepository.findById(1L).orElseThrow(); - - // then - assertThat(getMember.getCurrentNightCount()).isEqualTo(2); - } - - @DisplayName("방 상세 정보 조회 성공 테스트") - @WithMember(id = 1L) - @Test - void get_room_details_test() throws Exception { - // given - Room room = Room.builder() - .title("방 제목") - .password("1234") - .roomType(NIGHT) - .certifyTime(23) - .maxUserCount(5) - .build(); - - room.increaseCurrentUserCount(); - room.increaseCurrentUserCount(); - - List routines = RoomFixture.routines(room); - - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - - Member member2 = MemberFixture.member("2"); - Member member3 = MemberFixture.member("3"); - - roomRepository.save(room); - routineRepository.saveAll(routines); - member2 = memberRepository.save(member2); - member3 = memberRepository.save(member3); - - Item item = ItemFixture.nightMageSkin(); - - Inventory inventory1 = InventoryFixture.inventory(1L, item); - Inventory inventory2 = InventoryFixture.inventory(member2.getId(), item); - Inventory inventory3 = InventoryFixture.inventory(member3.getId(), item); - inventory1.select(member); - inventory2.select(member2); - inventory3.select(member3); - - itemRepository.save(item); - inventoryRepository.saveAll(List.of(inventory1, inventory2, inventory3)); - - Participant participant2 = RoomFixture.participant(room, member2.getId()); - Participant participant3 = RoomFixture.participant(room, member3.getId()); - - participantRepository.save(participant1); - participantRepository.save(participant2); - participantRepository.save(participant3); - - Certification certification1 = Certification.builder() - .routine(routines.get(0)) - .memberId(member.getId()) - .image("member1Image") - .build(); - - Certification certification2 = Certification.builder() - .routine(routines.get(1)) - .memberId(member.getId()) - .image("member2Image") - .build(); - - certificationRepository.save(certification1); - certificationRepository.save(certification2); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member.getId(), - room.getId(), participant1); - dailyMemberCertificationRepository.save(dailyMemberCertification); - - DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(room.getId(), - LocalDate.now()); - dailyRoomCertificationRepository.save(dailyRoomCertification); - - DailyRoomCertification dailyRoomCertification1 = RoomFixture.dailyRoomCertification(room.getId(), - LocalDate.now().minusDays(3)); - dailyRoomCertificationRepository.save(dailyRoomCertification1); - - // expected - mockMvc.perform(get("/rooms/" + room.getId() + "/" + LocalDate.now())) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 추방 성공") - @WithMember(id = 1L) - @Test - void deport_member_success() throws Exception { - // given - Room room = RoomFixture.room(); - Member member = MemberFixture.member("1234"); - memberRepository.save(member); - - Participant memberParticipant = RoomFixture.participant(room, member.getId()); - Participant managerParticipant = RoomFixture.participant(room, 1L); - managerParticipant.enableManager(); - - room.increaseCurrentUserCount(); - - roomRepository.save(room); - participantRepository.save(memberParticipant); - participantRepository.save(managerParticipant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) - .andExpect(status().isOk()) - .andDo(print()); - roomRepository.flush(); - - Room getRoom = roomRepository.findById(room.getId()).orElseThrow(); - Participant getMemberParticipant = participantRepository.findById(memberParticipant.getId()).orElseThrow(); - - assertThat(getRoom.getCurrentUserCount()).isEqualTo(1); - assertThat(getMemberParticipant.getDeletedAt()).isNotNull(); - assertThat(participantSearchRepository.findOne(member.getId(), room.getId())).isEmpty(); - } - - @DisplayName("방장 본인 추방 시도 - 예외 처리") - @WithMember(id = 1L) - @Test - void deport_self_fail() throws Exception { - // given - Room room = RoomFixture.room(); - - Participant managerParticipant = RoomFixture.participant(room, member.getId()); - managerParticipant.enableManager(); - - roomRepository.save(room); - participantRepository.save(managerParticipant); - - // expected - mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) - .andExpect(status().isBadRequest()) - .andDo(print()); - } - - @DisplayName("방장 위임 성공") - @WithMember(id = 1L) - @Test - void mandate_manager_success() throws Exception { - // given - Member member2 = MemberFixture.member("1234"); - memberRepository.save(member2); - - Room room = RoomFixture.room(); - Participant participant1 = RoomFixture.participant(room, member.getId()); - participant1.enableManager(); - Participant participant2 = RoomFixture.participant(room, member2.getId()); - - roomRepository.save(room); - participantRepository.save(participant1); - participantRepository.save(participant2); - - // expected - mockMvc.perform(put("/rooms/" + room.getId() + "/members/" + member2.getId() + "/mandate")) - .andExpect(status().isOk()) - .andDo(print()); - - Room savedRoom = roomRepository.findById(room.getId()).orElseThrow(); - Participant savedParticipant1 = participantRepository.findById(participant1.getId()).orElseThrow(); - Participant savedParticipant2 = participantRepository.findById(participant2.getId()).orElseThrow(); - - assertThat(savedRoom.getManagerNickname()).isEqualTo(member2.getNickname()); - assertThat(savedParticipant1.isManager()).isFalse(); - assertThat(savedParticipant2.isManager()).isTrue(); - } - - @DisplayName("현재 참여중인 모든 방 조회 성공 - 첫번째 방은 개인과 방 모두 인증 성공") - @WithMember(id = 1L) - @Test - void get_all_my_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); - Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); - Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); - - Participant participant1 = RoomFixture.participant(room1, 1L); - Participant participant2 = RoomFixture.participant(room2, 1L); - Participant participant3 = RoomFixture.participant(room3, 1L); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(1L, 1L, participant1); - DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(1L, LocalDate.now()); - - roomRepository.saveAll(List.of(room1, room2, room3)); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - dailyMemberCertificationRepository.save(dailyMemberCertification); - dailyRoomCertificationRepository.save(dailyRoomCertification); - - // expected - mockMvc.perform(get("/rooms/my-join")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 참여 기록 조회 성공") - @WithMember(id = 1L) - @Test - void get_join_history_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); - Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); - Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); - - Participant participant1 = RoomFixture.participant(room1, 1L); - Participant participant2 = RoomFixture.participant(room2, 1L); - Participant participant3 = RoomFixture.participant(room3, 1L); - - roomRepository.saveAll(List.of(room1, room2, room3)); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - - participant3.removeRoom(); - participantRepository.flush(); - participantRepository.delete(participant3); - - // expected - mockMvc.perform(get("/rooms/join-history")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중이지 않은 방에 대한 확인 성공") - @WithMember - @Test - void check_if_participant_false_success() throws Exception { - // given - Room room = RoomFixture.room(); - Room savedRoom = roomRepository.save(room); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중이지 않은 방의 정보 불러오기 성공") - @Test - void get_un_joined_room_details() throws Exception { - // given - Room room = RoomFixture.room("테스트 방", NIGHT, 21); - Room savedRoom = roomRepository.save(room); - - Member member1 = MemberFixture.member("901010"); - member1 = memberRepository.save(member1); - - Item item = ItemFixture.nightMageSkin(); - - Inventory inventory = InventoryFixture.inventory(member1.getId(), item); - inventory.select(member1); - - itemRepository.save(item); - inventoryRepository.save(inventory); - - Participant participant = RoomFixture.participant(savedRoom, member1.getId()); - participantRepository.save(participant); - - Routine routine1 = RoomFixture.routine(savedRoom, "물 마시기"); - Routine routine2 = RoomFixture.routine(savedRoom, "커피 마시기"); - routineRepository.saveAll(List.of(routine1, routine2)); - - DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member1.getId(), - savedRoom.getId(), participant); - dailyMemberCertificationRepository.save(dailyMemberCertification); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/un-joined")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("참여중인 방에 대한 확인 성공") - @WithMember - @Test - void check_if_participant_true_success() throws Exception { - // given - Room room = RoomFixture.room(); - Room savedRoom = roomRepository.save(room); - - Participant participant = RoomFixture.participant(room, 1L); - participantRepository.save(participant); - - // expected - mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침, 저녁 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 있음") - @WithMember(id = 1L) - @Test - void search_all_morning_night_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침, 저녁 방 전체 조회 성공 - 마지막 조회, 다음 페이지 없음") - @WithMember(id = 1L) - @Test - void search_last_page_all_morning_night_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RoomRepository roomRepository; + + @Autowired + private RoutineRepository routineRepository; + + @Autowired + private ParticipantRepository participantRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private CertificationRepository certificationRepository; + + @Autowired + private DailyMemberCertificationRepository dailyMemberCertificationRepository; + + @Autowired + private DailyRoomCertificationRepository dailyRoomCertificationRepository; + + @Autowired + private ParticipantSearchRepository participantSearchRepository; + + @Autowired + private ItemRepository itemRepository; + + @Autowired + private InventoryRepository inventoryRepository; + + @SpyBean + private SystemClockHolder clockHolder; + + Member member; + + @BeforeAll + void setUp() { + member = MemberFixture.member(); + memberRepository.save(member); + } + + @AfterEach + void cleanUp() { + while (member.getCurrentMorningCount() > 0) { + member.exitRoom(MORNING); + } + + while (member.getCurrentNightCount() > 0) { + member.exitRoom(NIGHT); + } + } + + @DisplayName("비밀번호 없는 방 생성 성공") + @WithMember + @Test + void create_room_no_password_success() throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isCreated()) + .andDo(print()); + + assertThat(roomRepository.findAll()).hasSize(1); + assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("재윤과 앵맹이의 방임"); + assertThat(roomRepository.findAll().get(0).getPassword()).isNull(); + } + + @DisplayName("비밀번호 있는 방 생성 성공") + @WithMember + @ParameterizedTest + @CsvSource({ + "1234", "12345678", "98765" + }) + void create_room_with_password_success(String password) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 있는 재맹의 방임", password, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isCreated()) + .andDo(print()); + + assertThat(roomRepository.findAll()).hasSize(1); + assertThat(roomRepository.findAll().get(0).getTitle()).isEqualTo("비번 있는 재맹의 방임"); + assertThat(roomRepository.findAll().get(0).getPassword()).isEqualTo(password); + } + + @DisplayName("올바르지 않은 비밀번호 방 생성시 예외 발생") + @ParameterizedTest + @CsvSource({ + "1", "12", "123", "123456789", "abc" + }) + void create_room_with_wrong_password_fail(String password) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 있는 재윤과 앵맹이의 방임", password, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("Routine 갯수를 초과한 방 생성시 예외 발생") + @Test + void create_room_with_too_many_routine_fail() throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + routines.add("밥 먹기"); + routines.add("코드 리뷰 달기"); + routines.add("책 읽기"); + routines.add("산책 하기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("Routine 없는 방 생성시 예외 발생") + @Test + void create_room_with_no_routine_fail() throws Exception { + // given + List routines = new ArrayList<>(); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, 10, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("올바르지 못한 시간으로 아침 방 생성시 예외 발생") + @ParameterizedTest + @CsvSource({ + "1", "3", "11", "12", "20" + }) + void create_morning_room_wrong_certify_time_fail(int certifyTime) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, MORNING, certifyTime, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("올바르지 못한 시간으로 저녁 방 생성시 에외 발생") + @ParameterizedTest + @CsvSource({ + "19", "3", "6", "9" + }) + void create_night_room_wrong_certify_time_fail(int certifyTime) throws Exception { + // given + List routines = new ArrayList<>(); + routines.add("물 마시기"); + routines.add("코테 풀기"); + + CreateRoomRequest createRoomRequest = new CreateRoomRequest( + "비번 없는 재윤과 앵맹이의 방임", null, routines, NIGHT, certifyTime, 4); + String json = objectMapper.writeValueAsString(createRoomRequest); + + // expected + mockMvc.perform(post("/rooms") + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("방 수정 성공 - 방장일 경우") + @WithMember(id = 1L) + @Test + void modify_room_success() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + List routines = RoomFixture.routines(room); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + roomRepository.save(room); + routineRepository.saveAll(routines); + participantRepository.save(participant); + + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "공지공지", "4567", 10, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + + Room modifiedRoom = roomRepository.findById(room.getId()).orElseThrow(); + List modifiedRoutines = routineRepository.findAllByRoomId(room.getId()); + + assertThat(modifiedRoom.getTitle()).isEqualTo("수정할 방임!"); + assertThat(modifiedRoom.getCertifyTime()).isEqualTo(10); + assertThat(modifiedRoom.getPassword()).isEqualTo("4567"); + assertThat(modifiedRoom.getAnnouncement()).isEqualTo("공지공지"); + assertThat(modifiedRoom.getMaxUserCount()).isEqualTo(7); + assertThat(modifiedRoutines).hasSize(2); + } + + @DisplayName("방 수정 실패 - 방장 아닐 경우") + @WithMember(id = 1L) + @Test + void unauthorized_modify_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + roomRepository.save(room); + participantRepository.save(participant); + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 9, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + String message = "{\"message\":\"방장이 아닌 사용자는 방을 수정할 수 없습니다.\"}"; + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isNotFound()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("방 수정 실패 - 이미 한 참여자가 인증하고 방의 인증 시간을 바꾸려고 할때 예외 처리") + @WithMember(id = 1L) + @Test + void room_certify_time_modification_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + room = roomRepository.save(room); + + Member member2 = MemberFixture.member("12313123"); + member2 = memberRepository.save(member2); + + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + + Participant participant2 = RoomFixture.participant(room, member2.getId()); + + participantRepository.saveAll(List.of(participant1, participant2)); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member2.getId(), + room.getId(), participant2); + + dailyMemberCertificationRepository.save(dailyMemberCertification); + + ModifyRoomRequest modifyRoomRequest = new ModifyRoomRequest("수정할 방임!", "방 공지", "1234", 10, 7); + String json = objectMapper.writeValueAsString(modifyRoomRequest); + + // expected + mockMvc.perform(put("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("비밀번호 있는 방 참여 성공") + @WithMember(id = 1L) + @Test + void enter_room_with_password_success() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("비밀번호 없는 방 참여 성공") + @WithMember(id = 1L) + @Test + void enter_room_with_no_password_success() throws Exception { + // given + BDDMockito.given(clockHolder.times()).willReturn(LocalDateTime.of(2023, 12, 3, 14, 30, 0)); + Room room = RoomFixture.room(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest(null); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 참여 후 인원수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_room_user_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); + + // then + assertThat(findRoom.getCurrentUserCount()).isEqualTo(2); + } + + @DisplayName("아침 방 참여 후 사용자의 방 입장 횟수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_morning_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentMorningCount()).isEqualTo(1); + assertThat(getMember.getCurrentNightCount()).isZero(); + } + + @DisplayName("저녁 방 참여 후 사용자의 방 입장 횟수 증가 테스트") + @WithMember(id = 1L) + @Test + void enter_and_increase_night_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(5) + .build(); + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentNightCount()).isEqualTo(1); + assertThat(getMember.getCurrentMorningCount()).isZero(); + } + + @DisplayName("사용자의 아침 방 입장 횟수 3일시 예외 처리") + @Test + void enter_and_morning_room_over_three_fail() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 3; i++) { + member.enterRoom(MORNING); + } + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()); + } + + @DisplayName("사용자의 저녁 방 입장 횟수 3일시 예외 처리") + @Test + void enter_and_night_room_over_three_fail() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(22) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 3; i++) { + member.enterRoom(NIGHT); + } + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + + // when + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()); + } + + @DisplayName("비밀번호 불일치 방 참여시 예외 발생") + @WithMember(id = 1L) + @Test + void enter_room_wrong_password_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + Member member = Member.builder() + .id(1L) + .socialId("1") + .bug(BugFixture.bug()) + .build(); + + memberRepository.save(member); + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("1234"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + String message = "{\"message\":\"방의 비밀번호가 일치하지 않습니다.\"}"; + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("인원수가 모두 찬 방 참여시 예외 발생") + @WithMember(id = 1L) + @Test + void enter_max_user_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("처음 제목") + .password("7777") + .roomType(MORNING) + .certifyTime(9) + .maxUserCount(5) + .build(); + + for (int i = 0; i < 4; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + EnterRoomRequest enterRoomRequest = new EnterRoomRequest("7777"); + String json = objectMapper.writeValueAsString(enterRoomRequest); + String message = "{\"message\":\"방의 인원수가 찼습니다.\"}"; + + // expected + mockMvc.perform(post("/rooms/" + room.getId()) + .contentType(APPLICATION_JSON) + .content(json)) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("일반 사용자의 방 나가기 성공") + @WithMember(id = 1L) + @Test + void no_manager_exit_room_success() throws Exception { + // given + Room room = Room.builder() + .title("5명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(8) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 4; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + participantRepository.save(participant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + + Room findRoom = roomRepository.findById(room.getId()).orElseThrow(); + List deletedParticipant = participantRepository.findAll(); + + assertThat(findRoom.getCurrentUserCount()).isEqualTo(4); + assertThat(deletedParticipant).hasSize(1); + assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); + assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); + } + + @DisplayName("방장의 방 나가기 - 방 삭제 성공") + @WithMember(id = 1L) + @Test + void manager_delete_room_success() throws Exception { + // given + Room room = Room.builder() + .title("1명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(8) + .build(); + + List routines = RoomFixture.routines(room); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + roomRepository.save(room); + routineRepository.saveAll(routines); + participantRepository.save(participant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + + List deletedRoom = roomRepository.findAll(); + List deletedRoutine = routineRepository.findAll(); + List deletedParticipant = participantRepository.findAll(); + + assertThat(deletedRoom).isEmpty(); + assertThat(deletedRoutine).hasSize(0); + assertThat(deletedParticipant).hasSize(1); + assertThat(deletedParticipant.get(0).getDeletedAt()).isNotNull(); + assertThat(deletedParticipant.get(0).getDeletedRoomTitle()).isNotNull(); + } + + @DisplayName("방장이 위임하지 않고 방 나가기 실패") + @WithMember(id = 1L) + @Test + void manager_exit_room_fail() throws Exception { + // given + Room room = Room.builder() + .title("7명이 있는 방~") + .roomType(NIGHT) + .certifyTime(21) + .maxUserCount(10) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + participant.enableManager(); + + for (int i = 0; i < 6; i++) { + room.increaseCurrentUserCount(); + } + + roomRepository.save(room); + participantRepository.save(participant); + String message = "{\"message\":\"인원수가 2명 이상일 때는 방장을 위임해야 합니다.\"}"; + + // expected + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isBadRequest()) + .andExpect(content().json(message)) + .andDo(print()); + } + + @DisplayName("아침 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") + @WithMember(id = 1L) + @Test + void exit_and_decrease_morning_room_count() throws Exception { + // given + Room room = RoomFixture.room(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 3; i++) { + member.enterRoom(RoomType.MORNING); + } + + memberRepository.save(member); + roomRepository.save(room); + participantRepository.save(participant); + + // when + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentMorningCount()).isEqualTo(2); + } + + @DisplayName("저녁 방 나가기 이후 사용자의 방 입장 횟수 감소 테스트") + @WithMember(id = 1L) + @Test + void exit_and_decrease_night_room_count() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(23) + .maxUserCount(5) + .build(); + + Participant participant = RoomFixture.participant(room, 1L); + + for (int i = 0; i < 3; i++) { + member.enterRoom(NIGHT); + } + + memberRepository.save(member); + roomRepository.save(room); + participantRepository.save(participant); + + // when + mockMvc.perform(delete("/rooms/" + room.getId())) + .andExpect(status().isOk()); + + Member getMember = memberRepository.findById(1L).orElseThrow(); + + // then + assertThat(getMember.getCurrentNightCount()).isEqualTo(2); + } + + @DisplayName("방 상세 정보 조회 성공 테스트") + @WithMember(id = 1L) + @Test + void get_room_details_test() throws Exception { + // given + Room room = Room.builder() + .title("방 제목") + .password("1234") + .roomType(NIGHT) + .certifyTime(23) + .maxUserCount(5) + .build(); + + room.increaseCurrentUserCount(); + room.increaseCurrentUserCount(); + + List routines = RoomFixture.routines(room); + + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + + Member member2 = MemberFixture.member("2"); + Member member3 = MemberFixture.member("3"); + + roomRepository.save(room); + routineRepository.saveAll(routines); + member2 = memberRepository.save(member2); + member3 = memberRepository.save(member3); + + Item item = ItemFixture.nightMageSkin(); + + Inventory inventory1 = InventoryFixture.inventory(1L, item); + Inventory inventory2 = InventoryFixture.inventory(member2.getId(), item); + Inventory inventory3 = InventoryFixture.inventory(member3.getId(), item); + inventory1.select(member); + inventory2.select(member2); + inventory3.select(member3); + + itemRepository.save(item); + inventoryRepository.saveAll(List.of(inventory1, inventory2, inventory3)); + + Participant participant2 = RoomFixture.participant(room, member2.getId()); + Participant participant3 = RoomFixture.participant(room, member3.getId()); + + participantRepository.save(participant1); + participantRepository.save(participant2); + participantRepository.save(participant3); + + Certification certification1 = Certification.builder() + .routine(routines.get(0)) + .memberId(member.getId()) + .image("member1Image") + .build(); + + Certification certification2 = Certification.builder() + .routine(routines.get(1)) + .memberId(member.getId()) + .image("member2Image") + .build(); + + certificationRepository.save(certification1); + certificationRepository.save(certification2); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member.getId(), + room.getId(), participant1); + dailyMemberCertificationRepository.save(dailyMemberCertification); + + DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(room.getId(), + LocalDate.now()); + dailyRoomCertificationRepository.save(dailyRoomCertification); + + DailyRoomCertification dailyRoomCertification1 = RoomFixture.dailyRoomCertification(room.getId(), + LocalDate.now().minusDays(3)); + dailyRoomCertificationRepository.save(dailyRoomCertification1); + + // expected + mockMvc.perform(get("/rooms/" + room.getId() + "/" + LocalDate.now())) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 추방 성공") + @WithMember(id = 1L) + @Test + void deport_member_success() throws Exception { + // given + Room room = RoomFixture.room(); + Member member = MemberFixture.member("1234"); + memberRepository.save(member); + + Participant memberParticipant = RoomFixture.participant(room, member.getId()); + Participant managerParticipant = RoomFixture.participant(room, 1L); + managerParticipant.enableManager(); + + room.increaseCurrentUserCount(); + + roomRepository.save(room); + participantRepository.save(memberParticipant); + participantRepository.save(managerParticipant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) + .andExpect(status().isOk()) + .andDo(print()); + roomRepository.flush(); + + Room getRoom = roomRepository.findById(room.getId()).orElseThrow(); + Participant getMemberParticipant = participantRepository.findById(memberParticipant.getId()).orElseThrow(); + + assertThat(getRoom.getCurrentUserCount()).isEqualTo(1); + assertThat(getMemberParticipant.getDeletedAt()).isNotNull(); + assertThat(participantSearchRepository.findOne(member.getId(), room.getId())).isEmpty(); + } + + @DisplayName("방장 본인 추방 시도 - 예외 처리") + @WithMember(id = 1L) + @Test + void deport_self_fail() throws Exception { + // given + Room room = RoomFixture.room(); + + Participant managerParticipant = RoomFixture.participant(room, member.getId()); + managerParticipant.enableManager(); + + roomRepository.save(room); + participantRepository.save(managerParticipant); + + // expected + mockMvc.perform(delete("/rooms/" + room.getId() + "/members/" + member.getId())) + .andExpect(status().isBadRequest()) + .andDo(print()); + } + + @DisplayName("방장 위임 성공") + @WithMember(id = 1L) + @Test + void mandate_manager_success() throws Exception { + // given + Member member2 = MemberFixture.member("1234"); + memberRepository.save(member2); + + Room room = RoomFixture.room(); + Participant participant1 = RoomFixture.participant(room, member.getId()); + participant1.enableManager(); + Participant participant2 = RoomFixture.participant(room, member2.getId()); + + roomRepository.save(room); + participantRepository.save(participant1); + participantRepository.save(participant2); + + // expected + mockMvc.perform(put("/rooms/" + room.getId() + "/members/" + member2.getId() + "/mandate")) + .andExpect(status().isOk()) + .andDo(print()); + + Room savedRoom = roomRepository.findById(room.getId()).orElseThrow(); + Participant savedParticipant1 = participantRepository.findById(participant1.getId()).orElseThrow(); + Participant savedParticipant2 = participantRepository.findById(participant2.getId()).orElseThrow(); + + assertThat(savedRoom.getManagerNickname()).isEqualTo(member2.getNickname()); + assertThat(savedParticipant1.isManager()).isFalse(); + assertThat(savedParticipant2.isManager()).isTrue(); + } + + @DisplayName("현재 참여중인 모든 방 조회 성공 - 첫번째 방은 개인과 방 모두 인증 성공") + @WithMember(id = 1L) + @Test + void get_all_my_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); + Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); + Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); + + Participant participant1 = RoomFixture.participant(room1, 1L); + Participant participant2 = RoomFixture.participant(room2, 1L); + Participant participant3 = RoomFixture.participant(room3, 1L); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(1L, 1L, participant1); + DailyRoomCertification dailyRoomCertification = RoomFixture.dailyRoomCertification(1L, LocalDate.now()); + + roomRepository.saveAll(List.of(room1, room2, room3)); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + dailyMemberCertificationRepository.save(dailyMemberCertification); + dailyRoomCertificationRepository.save(dailyRoomCertification); + + // expected + mockMvc.perform(get("/rooms/my-join")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 참여 기록 조회 성공") + @WithMember(id = 1L) + @Test + void get_join_history_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", MORNING, 10); + Room room2 = RoomFixture.room("아침 - 두 번째 방", MORNING, 8); + Room room3 = RoomFixture.room("밤 - 세 번째 방", NIGHT, 22); + + Participant participant1 = RoomFixture.participant(room1, 1L); + Participant participant2 = RoomFixture.participant(room2, 1L); + Participant participant3 = RoomFixture.participant(room3, 1L); + + roomRepository.saveAll(List.of(room1, room2, room3)); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + + participant3.removeRoom(); + participantRepository.flush(); + participantRepository.delete(participant3); + + // expected + mockMvc.perform(get("/rooms/join-history")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중이지 않은 방에 대한 확인 성공") + @WithMember + @Test + void check_if_participant_false_success() throws Exception { + // given + Room room = RoomFixture.room(); + Room savedRoom = roomRepository.save(room); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중이지 않은 방의 정보 불러오기 성공") + @Test + void get_un_joined_room_details() throws Exception { + // given + Room room = RoomFixture.room("테스트 방", NIGHT, 21); + Room savedRoom = roomRepository.save(room); + + Member member1 = MemberFixture.member("901010"); + member1 = memberRepository.save(member1); + + Item item = ItemFixture.nightMageSkin(); + + Inventory inventory = InventoryFixture.inventory(member1.getId(), item); + inventory.select(member1); + + itemRepository.save(item); + inventoryRepository.save(inventory); + + Participant participant = RoomFixture.participant(savedRoom, member1.getId()); + participantRepository.save(participant); + + Routine routine1 = RoomFixture.routine(savedRoom, "물 마시기"); + Routine routine2 = RoomFixture.routine(savedRoom, "커피 마시기"); + routineRepository.saveAll(List.of(routine1, routine2)); + + DailyMemberCertification dailyMemberCertification = RoomFixture.dailyMemberCertification(member1.getId(), + savedRoom.getId(), participant); + dailyMemberCertificationRepository.save(dailyMemberCertification); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/un-joined")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("참여중인 방에 대한 확인 성공") + @WithMember + @Test + void check_if_participant_true_success() throws Exception { + // given + Room room = RoomFixture.room(); + Room savedRoom = roomRepository.save(room); + + Participant participant = RoomFixture.participant(room, 1L); + participantRepository.save(participant); + + // expected + mockMvc.perform(get("/rooms/" + savedRoom.getId() + "/check")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침, 저녁 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 있음") + @WithMember(id = 1L) + @Test + void search_all_morning_night_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침, 저녁 방 전체 조회 성공 - 마지막 조회, 다음 페이지 없음") + @WithMember(id = 1L) + @Test + void search_last_page_all_morning_night_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms?roomId=5")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("아침 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 없음") - @WithMember(id = 1L) - @Test - void search_last_page_all_morning_rooms_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms?roomId=5")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("아침 방 전체 조회 성공 - 첫 번째 조회, 다음 페이지 없음") + @WithMember(id = 1L) + @Test + void search_last_page_all_morning_rooms_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms?roomType=MORNING")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드만 존재") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms?roomType=MORNING")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드만 존재") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=아침")) - .andExpect(status().isOk()) - .andDo(print()); - - mockMvc.perform(get("/rooms/search?keyword=방12")) - .andExpect(status().isOk()) - .andDo(print()); - - mockMvc.perform(get("/rooms/search?keyword=방")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 존재") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_roomType_success() throws Exception { - // given - Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); - Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=아침")) + .andExpect(status().isOk()) + .andDo(print()); + + mockMvc.perform(get("/rooms/search?keyword=방12")) + .andExpect(status().isOk()) + .andDo(print()); + + mockMvc.perform(get("/rooms/search?keyword=방")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 존재") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_roomType_success() throws Exception { + // given + Room room1 = RoomFixture.room("아침 - 첫 번째 방", RoomType.MORNING, 10, "1234"); + Room room2 = RoomFixture.room("아침 - 두 번째 방", RoomType.MORNING, 9); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("아침 - 아홉 번째 방", RoomType.MORNING, 4); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("아침 - 열둘 번째 방", RoomType.MORNING, 10); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=번째&roomType=MORNING")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 + 추가 페이지 존재X") - @WithMember(id = 1L) - @Test - void search_first_page_all_rooms_by_keyword_roomType_roomId_success() throws Exception { - // given - Room room1 = RoomFixture.room("밤 - 첫 번째 방", RoomType.NIGHT, 1, "1234"); - Room room2 = RoomFixture.room("밤 - 두 번째 방", RoomType.NIGHT, 1); - Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); - Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); - Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); - Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); - Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); - Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); - Room room9 = RoomFixture.room("밤 - 아홉 번째 방", RoomType.NIGHT, 1, "5236"); - Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); - Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); - Room room12 = RoomFixture.room("밤 - 열둘 번째 방", RoomType.NIGHT, 1); - Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); - Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); - - Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); - Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); - - Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); - Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); - - Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); - Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); - - Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); - Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); - - Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); - Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); - - Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); - Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); - - Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); - Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); - - Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); - Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); - - Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); - Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); - - Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); - Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); - - Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); - Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); - - Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); - Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); - - Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); - Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); - - Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); - Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); - - roomRepository.saveAll( - List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, - room14)); - - routineRepository.saveAll( - List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, - routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, - routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); - - // expected - mockMvc.perform(get("/rooms/search?keyword=루틴&roomType=NIGHT&roomId=3")) - .andExpect(status().isOk()) - .andDo(print()); - } - - @DisplayName("방 수정전 정보 불러오기 성공") - @WithMember(id = 1L) - @Test - void get_room_details_before_modification_success() throws Exception { - // given - Member member2 = MemberFixture.member("123"); - Member member3 = MemberFixture.member("456"); - member2 = memberRepository.save(member2); - member3 = memberRepository.save(member3); - - Room room = RoomFixture.room("수정 전 방 제목", MORNING, 10, "1234"); - Participant participant1 = RoomFixture.participant(room, 1L); - participant1.enableManager(); - Participant participant2 = RoomFixture.participant(room, member2.getId()); - Participant participant3 = RoomFixture.participant(room, member3.getId()); - List routines = RoomFixture.routines(room); - - roomRepository.save(room); - participantRepository.saveAll(List.of(participant1, participant2, participant3)); - routineRepository.saveAll(routines); - - // expected - mockMvc.perform(get("/rooms/" + room.getId())) - .andExpect(status().isOk()) - .andDo(print()); - } + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=번째&roomType=MORNING")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 검색 조회 성공 - 키워드 + 방 타입 + 추가 페이지 존재X") + @WithMember(id = 1L) + @Test + void search_first_page_all_rooms_by_keyword_roomType_roomId_success() throws Exception { + // given + Room room1 = RoomFixture.room("밤 - 첫 번째 방", RoomType.NIGHT, 1, "1234"); + Room room2 = RoomFixture.room("밤 - 두 번째 방", RoomType.NIGHT, 1); + Room room3 = RoomFixture.room("밤 - 세 번째 방", RoomType.NIGHT, 22); + Room room4 = RoomFixture.room("아침 - 네 번째 방", RoomType.MORNING, 7); + Room room5 = RoomFixture.room("밤 - 다섯 번째 방", RoomType.NIGHT, 23, "5869"); + Room room6 = RoomFixture.room("아침 - 여섯 번째 방", RoomType.MORNING, 8); + Room room7 = RoomFixture.room("밤 - 일곱 번째 방", RoomType.NIGHT, 20); + Room room8 = RoomFixture.room("밤 - 여덟 번째 방", RoomType.NIGHT, 1, "5236"); + Room room9 = RoomFixture.room("밤 - 아홉 번째 방", RoomType.NIGHT, 1, "5236"); + Room room10 = RoomFixture.room("밤 - 열 번째 방", RoomType.NIGHT, 1, "97979"); + Room room11 = RoomFixture.room("밤 - 열하나 번째 방", RoomType.NIGHT, 22); + Room room12 = RoomFixture.room("밤 - 열둘 번째 방", RoomType.NIGHT, 1); + Room room13 = RoomFixture.room("밤 - 열셋 번째 방", RoomType.NIGHT, 2); + Room room14 = RoomFixture.room("밤 - 열넷 번째 방", RoomType.NIGHT, 21); + + Routine routine1 = RoomFixture.routine(room1, "방1의 루틴1"); + Routine routine2 = RoomFixture.routine(room1, "방1의 루틴2"); + + Routine routine3 = RoomFixture.routine(room2, "방2의 루틴1"); + Routine routine4 = RoomFixture.routine(room2, "방2의 루틴2"); + + Routine routine5 = RoomFixture.routine(room3, "방3의 루틴1"); + Routine routine6 = RoomFixture.routine(room3, "방3의 루틴2"); + + Routine routine7 = RoomFixture.routine(room4, "방4의 루틴1"); + Routine routine8 = RoomFixture.routine(room4, "방4의 루틴2"); + + Routine routine9 = RoomFixture.routine(room5, "방5의 루틴1"); + Routine routine10 = RoomFixture.routine(room5, "방5의 루틴2"); + + Routine routine11 = RoomFixture.routine(room6, "방6의 루틴1"); + Routine routine12 = RoomFixture.routine(room6, "방6의 루틴2"); + + Routine routine13 = RoomFixture.routine(room7, "방7의 루틴1"); + Routine routine14 = RoomFixture.routine(room7, "방7의 루틴2"); + + Routine routine15 = RoomFixture.routine(room8, "방8의 루틴1"); + Routine routine16 = RoomFixture.routine(room8, "방8의 루틴2"); + + Routine routine17 = RoomFixture.routine(room9, "방9의 루틴1"); + Routine routine18 = RoomFixture.routine(room9, "방9의 루틴2"); + + Routine routine19 = RoomFixture.routine(room10, "방10의 루틴1"); + Routine routine20 = RoomFixture.routine(room10, "방10의 루틴2"); + + Routine routine21 = RoomFixture.routine(room11, "방11의 루틴1"); + Routine routine22 = RoomFixture.routine(room11, "방11의 루틴2"); + + Routine routine23 = RoomFixture.routine(room12, "방12의 루틴1"); + Routine routine24 = RoomFixture.routine(room12, "방12의 루틴2"); + + Routine routine25 = RoomFixture.routine(room13, "방13의 루틴1"); + Routine routine26 = RoomFixture.routine(room13, "방13의 루틴2"); + + Routine routine27 = RoomFixture.routine(room14, "방14의 루틴1"); + Routine routine28 = RoomFixture.routine(room14, "방14의 루틴2"); + + roomRepository.saveAll( + List.of(room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, + room14)); + + routineRepository.saveAll( + List.of(routine1, routine2, routine3, routine4, routine5, routine6, routine7, routine8, routine9, routine10, + routine11, routine12, routine13, routine14, routine15, routine16, routine17, routine18, routine19, + routine20, routine21, routine22, routine23, routine24, routine25, routine26, routine27, routine28)); + + // expected + mockMvc.perform(get("/rooms/search?keyword=루틴&roomType=NIGHT&roomId=3")) + .andExpect(status().isOk()) + .andDo(print()); + } + + @DisplayName("방 수정전 정보 불러오기 성공") + @WithMember(id = 1L) + @Test + void get_room_details_before_modification_success() throws Exception { + // given + Member member2 = MemberFixture.member("123"); + Member member3 = MemberFixture.member("456"); + member2 = memberRepository.save(member2); + member3 = memberRepository.save(member3); + + Room room = RoomFixture.room("수정 전 방 제목", MORNING, 10, "1234"); + Participant participant1 = RoomFixture.participant(room, 1L); + participant1.enableManager(); + Participant participant2 = RoomFixture.participant(room, member2.getId()); + Participant participant3 = RoomFixture.participant(room, member3.getId()); + List routines = RoomFixture.routines(room); + + roomRepository.save(room); + participantRepository.saveAll(List.of(participant1, participant2, participant3)); + routineRepository.saveAll(routines); + + // expected + mockMvc.perform(get("/rooms/" + room.getId())) + .andExpect(status().isOk()) + .andDo(print()); + } } diff --git a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java index e4bfa647..3c4d35d0 100644 --- a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java +++ b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java @@ -1,40 +1,40 @@ package com.moabam.support.fixture; -import static com.moabam.global.common.util.BaseImageUrl.*; +import com.moabam.api.domain.member.BadgeType; +import com.moabam.api.dto.member.MemberInfo; import java.util.List; -import com.moabam.api.domain.member.BadgeType; -import com.moabam.api.dto.member.MemberInfo; +import static com.moabam.global.common.util.BaseImageUrl.*; public class MemberInfoSearchFixture { - private static final String NICKNAME = "nickname"; - private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; - private static final String INTRO = "intro"; - private static final long TOTAL_CERTIFY_COUNT = 15; - private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; - private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; - - public static List friendMemberInfo() { - return friendMemberInfo(TOTAL_CERTIFY_COUNT); - } - - public static List friendMemberInfo(long total) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.MORNING_BIRTH, - 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.NIGHT_BIRTH, - 0, 0, 0) - ); - } - - public static List myInfo(String morningImage, String nightImage) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.MORNING_BIRTH, 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.NIGHT_BIRTH, 0, 0, 0) - ); - } + private static final String NICKNAME = "nickname"; + private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; + private static final String INTRO = "intro"; + private static final long TOTAL_CERTIFY_COUNT = 15; + private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; + private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; + + public static List friendMemberInfo() { + return friendMemberInfo(TOTAL_CERTIFY_COUNT); + } + + public static List friendMemberInfo(long total) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.BIRTH, + 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.LEVEL10, + 0, 0, 0) + ); + } + + public static List myInfo(String morningImage, String nightImage) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.BIRTH, 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.LEVEL10, 0, 0, 0) + ); + } } From c627fe5013ab16fda67c82d78f789dad4826ac9d Mon Sep 17 00:00:00 2001 From: parksey Date: Sun, 3 Dec 2023 13:08:34 +0900 Subject: [PATCH 19/20] =?UTF-8?q?refactor:=20config=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/config b/src/main/resources/config index b217c205..3aa15e1b 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit b217c205120a8b9f25277ec1819c7141bbb5591c +Subproject commit 3aa15e1b92cc4573ccb5f18f120fb98ab66b48fa From 4f5123683879bb91152a3bcaca7c4c2355f3faac Mon Sep 17 00:00:00 2001 From: parksey Date: Sun, 3 Dec 2023 14:31:09 +0900 Subject: [PATCH 20/20] =?UTF-8?q?refactor:=20=EC=BD=94=EB=94=A9=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9E=AC=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/application/admin/AdminMapper.java | 4 + .../auth/AuthorizationService.java | 405 +++++------ .../auth/JwtAuthenticationService.java | 80 ++- .../application/ranking/RankingMapper.java | 51 +- .../application/ranking/RankingService.java | 95 +-- .../auth/repository/TokenRepository.java | 51 +- .../auth/filter/AuthorizationFilter.java | 203 +++--- .../moabam/global/auth/filter/CorsFilter.java | 111 ++- .../global/config/AllowOriginConfig.java | 11 +- src/main/resources/static/docs/coupon.html | 12 +- src/main/resources/static/docs/index.html | 2 +- .../resources/static/docs/notification.html | 2 +- .../auth/AuthorizationServiceTest.java | 679 +++++++++--------- .../presentation/RankingControllerTest.java | 131 ++-- .../common/WithoutFilterSupporter.java | 53 +- .../fixture/MemberInfoSearchFixture.java | 62 +- 16 files changed, 984 insertions(+), 968 deletions(-) diff --git a/src/main/java/com/moabam/admin/application/admin/AdminMapper.java b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java index d9cf0b35..648c2e4d 100644 --- a/src/main/java/com/moabam/admin/application/admin/AdminMapper.java +++ b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java @@ -2,6 +2,10 @@ import com.moabam.admin.domain.admin.Admin; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class AdminMapper { public static Admin toAdmin(Long socialId) { diff --git a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java index 4e040d07..3966d2c4 100644 --- a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java +++ b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java @@ -1,5 +1,14 @@ package com.moabam.api.application.auth; +import java.util.Arrays; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthMapper; import com.moabam.api.application.auth.mapper.AuthorizationMapper; @@ -7,7 +16,13 @@ import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.*; +import com.moabam.api.dto.auth.AuthorizationCodeRequest; +import com.moabam.api.dto.auth.AuthorizationCodeResponse; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.AuthorizationTokenRequest; +import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.api.dto.auth.LoginResponse; +import com.moabam.api.dto.auth.TokenSaveValue; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; @@ -19,214 +34,202 @@ import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponentsBuilder; - -import java.util.Arrays; @Slf4j @Service @RequiredArgsConstructor public class AuthorizationService { - private final FcmService fcmService; - private final OAuthConfig oAuthConfig; - private final TokenConfig tokenConfig; - private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; - private final MemberService memberService; - private final AdminService adminService; - private final JwtProviderService jwtProviderService; - private final TokenRepository tokenRepository; - private final AllowOriginConfig allowOriginsConfig; - - public void redirectToLoginPage(HttpServletResponse httpServletResponse) { - String authorizationCodeUri = getAuthorizationCodeUri(); - oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); - } - - public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), - oAuthConfig.provider().adminRedirectUri()); - } - - public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); - } - - public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { - String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); - ResponseEntity authorizationTokenInfoResponse = - oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); - - return authorizationTokenInfoResponse.getBody(); - } - - public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, - AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { - LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); - issueServiceToken(httpServletResponse, loginResponse.publicClaim()); - - return loginResponse; - } - - public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { - String accessToken = jwtProviderService.provideAccessToken(publicClaim); - String refreshToken = jwtProviderService.provideRefreshToken(publicClaim.role()); - TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); - - tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest, publicClaim.role()); - - String domain = getDomain(publicClaim.role()); - - response.addCookie( - CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire(), domain)); - response.addCookie( - CookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire(), domain)); - response.addCookie( - CookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire(), domain)); - } - - public void validTokenPair(Long id, String oldRefreshToken, Role role) { - TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id, role); - - if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { - tokenRepository.delete(id, role); - - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, - HttpServletResponse httpServletResponse) { - removeToken(httpServletRequest, httpServletResponse); - tokenRepository.delete(authMember.id(), authMember.role()); - fcmService.deleteTokenByMemberId(authMember.id()); - } - - public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - if (httpServletRequest.getCookies() == null) { - return; - } - - Arrays.stream(httpServletRequest.getCookies()) - .forEach(cookie -> { - if (cookie.getName().contains("token")) { - httpServletResponse.addCookie(CookieUtils.deleteCookie(cookie)); - } - }); - } - - @Transactional - public void unLinkMember(AuthMember authMember) { - Member member = memberService.findMemberToDelete(authMember.id()); - unlinkRequest(member.getSocialId()); - memberService.delete(member); - } - - private String getDomain(Role role) { - if (role.equals(Role.ADMIN)) { - return allowOriginsConfig.adminDomain(); - } - - return allowOriginsConfig.domain(); - } - - private void unlinkRequest(String socialId) { - try { - oauth2AuthorizationServerRequestService.unlinkMemberRequest( - oAuthConfig.provider().unlink(), - oAuthConfig.client().adminKey(), - unlinkRequestParam(socialId)); - log.info("회원 탈퇴 성공 : [socialId={}]", socialId); - } catch (BadRequestException badRequestException) { - log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); - throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); - } - } - - private MultiValueMap unlinkRequestParam(String socialId) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("target_id_type", "user_id"); - params.add("target_id", socialId); - - return params; - } - - private String getAuthorizationCodeUri() { - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); - return generateQueryParamsWith(authorizationCodeRequest); - } - - private String generateTokenValue(String token) { - return "Bearer" + GlobalConstant.SPACE + token; - } - - private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { - UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( - oAuthConfig.provider().authorizationUri()) - .queryParam("response_type", "code") - .queryParam("client_id", authorizationCodeRequest.clientId()) - .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); - - if (authorizationCodeRequest.scope() != null - && !authorizationCodeRequest.scope().isEmpty()) { - String scopes = String.join(",", authorizationCodeRequest.scope()); - authorizationCodeUri.queryParam("scope", scopes); - } - - return authorizationCodeUri.toUriString(); - } - - private void validAuthorizationGrant(String code) { - if (code == null) { - throw new BadRequestException(ErrorMessage.GRANT_FAILED); - } - } - - private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oAuthConfig, code, redirectUri); - MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); - ResponseEntity authorizationTokenResponse = - oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(), - uriParams); - - return authorizationTokenResponse.getBody(); - } - - private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { - MultiValueMap contents = new LinkedMultiValueMap<>(); - contents.add("grant_type", authorizationTokenRequest.grantType()); - contents.add("client_id", authorizationTokenRequest.clientId()); - contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); - contents.add("code", authorizationTokenRequest.code()); - - if (authorizationTokenRequest.clientSecret() != null) { - contents.add("client_secret", authorizationTokenRequest.clientSecret()); - } - - return contents; - } - - public void validMemberExist(Long id, Role role) { - if (role.equals(Role.ADMIN)) { - adminService.findMember(id); - - return; - } - - memberService.findMember(id); - } + private final FcmService fcmService; + private final OAuthConfig oAuthConfig; + private final TokenConfig tokenConfig; + private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; + private final MemberService memberService; + private final AdminService adminService; + private final JwtProviderService jwtProviderService; + private final TokenRepository tokenRepository; + private final AllowOriginConfig allowOriginsConfig; + + public void redirectToLoginPage(HttpServletResponse httpServletResponse) { + String authorizationCodeUri = getAuthorizationCodeUri(); + oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); + } + + public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), + oAuthConfig.provider().adminRedirectUri()); + } + + public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); + } + + public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { + String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); + ResponseEntity authorizationTokenInfoResponse = + oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); + + return authorizationTokenInfoResponse.getBody(); + } + + public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, + AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); + issueServiceToken(httpServletResponse, loginResponse.publicClaim()); + + return loginResponse; + } + + public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { + String accessToken = jwtProviderService.provideAccessToken(publicClaim); + String refreshToken = jwtProviderService.provideRefreshToken(publicClaim.role()); + TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); + + tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest, publicClaim.role()); + + String domain = getDomain(publicClaim.role()); + + response.addCookie(CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire(), domain)); + } + + public void validTokenPair(Long id, String oldRefreshToken, Role role) { + TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id, role); + + if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { + tokenRepository.delete(id, role); + + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + removeToken(httpServletRequest, httpServletResponse); + tokenRepository.delete(authMember.id(), authMember.role()); + fcmService.deleteTokenByMemberId(authMember.id()); + } + + public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + if (httpServletRequest.getCookies() == null) { + return; + } + + Arrays.stream(httpServletRequest.getCookies()).forEach(cookie -> { + if (cookie.getName().contains("token")) { + httpServletResponse.addCookie(CookieUtils.deleteCookie(cookie)); + } + }); + } + + @Transactional + public void unLinkMember(AuthMember authMember) { + Member member = memberService.findMemberToDelete(authMember.id()); + unlinkRequest(member.getSocialId()); + memberService.delete(member); + } + + private String getDomain(Role role) { + if (role.equals(Role.ADMIN)) { + return allowOriginsConfig.adminDomain(); + } + + return allowOriginsConfig.domain(); + } + + private void unlinkRequest(String socialId) { + try { + oauth2AuthorizationServerRequestService.unlinkMemberRequest(oAuthConfig.provider().unlink(), + oAuthConfig.client().adminKey(), unlinkRequestParam(socialId)); + log.info("회원 탈퇴 성공 : [socialId={}]", socialId); + } catch (BadRequestException badRequestException) { + log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); + throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); + } + } + + private MultiValueMap unlinkRequestParam(String socialId) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("target_id_type", "user_id"); + params.add("target_id", socialId); + + return params; + } + + private String getAuthorizationCodeUri() { + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); + return generateQueryParamsWith(authorizationCodeRequest); + } + + private String generateTokenValue(String token) { + return "Bearer" + GlobalConstant.SPACE + token; + } + + private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { + UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( + oAuthConfig.provider().authorizationUri()) + .queryParam("response_type", "code") + .queryParam("client_id", authorizationCodeRequest.clientId()) + .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); + + if (authorizationCodeRequest.scope() != null && !authorizationCodeRequest.scope().isEmpty()) { + String scopes = String.join(",", authorizationCodeRequest.scope()); + authorizationCodeUri.queryParam("scope", scopes); + } + + return authorizationCodeUri.toUriString(); + } + + private void validAuthorizationGrant(String code) { + if (code == null) { + throw new BadRequestException(ErrorMessage.GRANT_FAILED); + } + } + + private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oAuthConfig, code, redirectUri); + MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); + ResponseEntity authorizationTokenResponse = + oauth2AuthorizationServerRequestService + .requestAuthorizationServer(oAuthConfig.provider().tokenUri(), uriParams); + + return authorizationTokenResponse.getBody(); + } + + private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { + MultiValueMap contents = new LinkedMultiValueMap<>(); + contents.add("grant_type", authorizationTokenRequest.grantType()); + contents.add("client_id", authorizationTokenRequest.clientId()); + contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); + contents.add("code", authorizationTokenRequest.code()); + + if (authorizationTokenRequest.clientSecret() != null) { + contents.add("client_secret", authorizationTokenRequest.clientSecret()); + } + + return contents; + } + + public void validMemberExist(Long id, Role role) { + if (role.equals(Role.ADMIN)) { + adminService.findMember(id); + + return; + } + + memberService.findMember(id); + } } diff --git a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java index a2b75462..52c652f9 100644 --- a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java +++ b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java @@ -1,57 +1,59 @@ package com.moabam.api.application.auth; +import java.nio.charset.StandardCharsets; +import java.security.Key; + +import org.json.JSONObject; +import org.springframework.stereotype.Service; + import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.io.Decoders; import lombok.RequiredArgsConstructor; -import org.json.JSONObject; -import org.springframework.stereotype.Service; - -import java.nio.charset.StandardCharsets; -import java.security.Key; @Service @RequiredArgsConstructor public class JwtAuthenticationService { - private final TokenConfig tokenConfig; - - public boolean isTokenExpire(String token, Role role) { - try { - Key key = getSecret(role); - - Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token); - return false; - } catch (ExpiredJwtException expiredJwtException) { - return true; - } catch (Exception exception) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - private Key getSecret(Role role) { - if (role.equals(Role.ADMIN)) { - return tokenConfig.getAdminKey(); - } - - return tokenConfig.getKey(); - } - - public PublicClaim parseClaim(String token) { - String claims = token.split("\\.")[1]; - byte[] claimsBytes = Decoders.BASE64URL.decode(claims); - String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); - JSONObject jsonObject = new JSONObject(decodedClaims); - - return AuthorizationMapper.toPublicClaim(jsonObject); - } + private final TokenConfig tokenConfig; + + public boolean isTokenExpire(String token, Role role) { + try { + Key key = getSecret(role); + + Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return false; + } catch (ExpiredJwtException expiredJwtException) { + return true; + } catch (Exception exception) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + private Key getSecret(Role role) { + if (role.equals(Role.ADMIN)) { + return tokenConfig.getAdminKey(); + } + + return tokenConfig.getKey(); + } + + public PublicClaim parseClaim(String token) { + String claims = token.split("\\.")[1]; + byte[] claimsBytes = Decoders.BASE64URL.decode(claims); + String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); + JSONObject jsonObject = new JSONObject(decodedClaims); + + return AuthorizationMapper.toPublicClaim(jsonObject); + } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index 5246815f..817ccdc8 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -1,42 +1,39 @@ package com.moabam.api.application.ranking; +import java.util.List; + import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; + import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.List; - @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RankingMapper { - public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { - return TopRankingInfo.builder() - .rank(rank) - .score(score) - .nickname(rankInfo.nickname()) - .image(rankInfo.image()) - .memberId(rankInfo.memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { + return TopRankingInfo.builder() + .rank(rank) + .score(score) + .nickname(rankInfo.nickname()) + .image(rankInfo.image()) + .memberId(rankInfo.memberId()) + .build(); + } - public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { - return TopRankingInfo.builder() - .rank(rank + 1) - .score(updateRanking.score()) - .nickname(updateRanking.rankingInfo().nickname()) - .image(updateRanking.rankingInfo().image()) - .memberId(updateRanking.rankingInfo().memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { + return TopRankingInfo.builder() + .rank(rank + 1) + .score(updateRanking.score()) + .nickname(updateRanking.rankingInfo().nickname()) + .image(updateRanking.rankingInfo().image()) + .memberId(updateRanking.rankingInfo().memberId()) + .build(); + } - public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, - List topRankings) { - return TopRankingResponse.builder() - .topRankings(topRankings) - .myRanking(myRanking) - .build(); - } + public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, List topRankings) { + return TopRankingResponse.builder().topRankings(topRankings).myRanking(myRanking).build(); + } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index 77dd7956..65e620a3 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -1,74 +1,75 @@ package com.moabam.api.application.ranking; +import static java.util.Objects.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static java.util.Objects.requireNonNull; +import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class RankingService { - private static final String RANKING = "Ranking"; - private static final int START_INDEX = 0; - private static final int LIMIT_INDEX = 9; + private static final String RANKING = "Ranking"; + private static final int START_INDEX = 0; + private static final int LIMIT_INDEX = 9; - private final ObjectMapper objectMapper; - private final ZSetRedisRepository zSetRedisRepository; + private final ObjectMapper objectMapper; + private final ZSetRedisRepository zSetRedisRepository; - public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { - zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); - } + public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { + zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); + } - public void updateScores(List updateRankings) { - updateRankings.forEach(updateRanking -> - zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); - } + public void updateScores(List updateRankings) { + updateRankings.forEach( + updateRanking -> zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); + } - public void changeInfos(RankingInfo before, RankingInfo after) { - zSetRedisRepository.changeMember(RANKING, before, after); - } + public void changeInfos(RankingInfo before, RankingInfo after) { + zSetRedisRepository.changeMember(RANKING, before, after); + } - public void removeRanking(RankingInfo rankingInfo) { - zSetRedisRepository.delete(RANKING, rankingInfo); - } + public void removeRanking(RankingInfo rankingInfo) { + zSetRedisRepository.delete(RANKING, rankingInfo); + } - public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { - List topRankings = getTopRankings(); - Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); - TopRankingInfo myRankingInfoResponse = - RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); + public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { + List topRankings = getTopRankings(); + Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); + TopRankingInfo myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); - return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); - } + return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); + } - private List getTopRankings() { - Set> topRankings = - zSetRedisRepository.rangeJson(RANKING, START_INDEX, LIMIT_INDEX); + private List getTopRankings() { + Set> topRankings = zSetRedisRepository.rangeJson(RANKING, START_INDEX, + LIMIT_INDEX); - Set scoreSet = new HashSet<>(); - List topRankingInfo = new ArrayList<>(); + Set scoreSet = new HashSet<>(); + List topRankingInfo = new ArrayList<>(); - for (ZSetOperations.TypedTuple topRanking : topRankings) { - long score = requireNonNull(topRanking.getScore()).longValue(); - scoreSet.add(score); + for (ZSetOperations.TypedTuple topRanking : topRankings) { + long score = requireNonNull(topRanking.getScore()).longValue(); + scoreSet.add(score); - RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); - topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); - } + RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); + topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + } - return topRankingInfo; - } + return topRankingInfo; + } } diff --git a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java index 0e5fba58..e104dad0 100644 --- a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java +++ b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java @@ -1,42 +1,43 @@ package com.moabam.api.domain.auth.repository; -import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.TokenSaveValue; -import com.moabam.api.infrastructure.redis.HashRedisRepository; +import java.time.Duration; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; -import java.time.Duration; +import com.moabam.api.domain.member.Role; +import com.moabam.api.dto.auth.TokenSaveValue; +import com.moabam.api.infrastructure.redis.HashRedisRepository; @Repository public class TokenRepository { - private static final int EXPIRE_DAYS = 14; + private static final int EXPIRE_DAYS = 14; - private final HashRedisRepository hashRedisRepository; + private final HashRedisRepository hashRedisRepository; - @Autowired - public TokenRepository(HashRedisRepository hashRedisRepository) { - this.hashRedisRepository = hashRedisRepository; - } + @Autowired + public TokenRepository(HashRedisRepository hashRedisRepository) { + this.hashRedisRepository = hashRedisRepository; + } - public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest, Role role) { - String tokenKey = parseTokenKey(memberId, role); + public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest, Role role) { + String tokenKey = parseTokenKey(memberId, role); - hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); - } + hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); + } - public TokenSaveValue getTokenSaveValue(Long memberId, Role role) { - String tokenKey = parseTokenKey(memberId, role); - return (TokenSaveValue) hashRedisRepository.get(tokenKey); - } + public TokenSaveValue getTokenSaveValue(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + return (TokenSaveValue)hashRedisRepository.get(tokenKey); + } - public void delete(Long memberId, Role role) { - String tokenKey = parseTokenKey(memberId, role); - hashRedisRepository.delete(tokenKey); - } + public void delete(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + hashRedisRepository.delete(tokenKey); + } - private String parseTokenKey(Long memberId, Role role) { - return role.name() + "_" + memberId; - } + private String parseTokenKey(Long memberId, Role role) { + return role.name() + "_" + memberId; + } } diff --git a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java index ded3a74a..ecc5438e 100644 --- a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java @@ -1,5 +1,15 @@ package com.moabam.global.auth.filter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + import com.moabam.api.application.auth.AuthorizationService; import com.moabam.api.application.auth.JwtAuthenticationService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; @@ -9,6 +19,7 @@ import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -17,15 +28,6 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; @Slf4j @Order(2) @@ -33,95 +35,96 @@ @RequiredArgsConstructor public class AuthorizationFilter extends OncePerRequestFilter { - private final HandlerExceptionResolver handlerExceptionResolver; - private final JwtAuthenticationService authenticationService; - private final AuthorizationService authorizationService; - - @Override - protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, - @NotNull HttpServletResponse httpServletResponse, - @NotNull FilterChain filterChain) throws ServletException, IOException { - - if (isPermit(httpServletRequest)) { - filterChain.doFilter(httpServletRequest, httpServletResponse); - return; - } - - try { - invoke(httpServletRequest, httpServletResponse); - } catch (UnauthorizedException unauthorizedException) { - authorizationService.removeToken(httpServletRequest, httpServletResponse); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - private boolean isPermit(HttpServletRequest httpServletRequest) { - Boolean isPermit = (Boolean) httpServletRequest.getAttribute("isPermit"); - - return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); - } - - private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - Cookie[] cookies = getCookiesOrThrow(httpServletRequest); - - if (!isTokenTypeBearer(cookies)) { - throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); - } - - handleTokenAuthenticate(cookies, httpServletResponse, httpServletRequest); - } - - private boolean isTokenTypeBearer(Cookie[] cookies) { - return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); - } - - private void handleTokenAuthenticate(Cookie[] cookies, - HttpServletResponse httpServletResponse, HttpServletRequest httpServletRequest) { - String accessToken = extractTokenFromCookie(cookies, "access_token"); - PublicClaim publicClaim = authenticationService.parseClaim(accessToken); - - if (authenticationService.isTokenExpire(accessToken, publicClaim.role())) { - String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); - - if (authenticationService.isTokenExpire(refreshToken, publicClaim.role())) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - - validInvalidMember(publicClaim, refreshToken, httpServletRequest); - authorizationService.issueServiceToken(httpServletResponse, publicClaim); - } - - AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); - } - - private void validInvalidMember(PublicClaim publicClaim, String refreshToken, - HttpServletRequest httpServletRequest) { - boolean isAdminPath = httpServletRequest.getRequestURI().contains("admins"); - - if (!((publicClaim.role().equals(Role.ADMIN) && isAdminPath) - || (publicClaim.role().equals(Role.USER) && !isAdminPath))) { - throw new BadRequestException(ErrorMessage.INVALID_REQUEST_ROLE); - } - - authorizationService.validTokenPair(publicClaim.id(), refreshToken, publicClaim.role()); - authorizationService.validMemberExist(publicClaim.id(), publicClaim.role()); - } - - private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { - return Optional.ofNullable(httpServletRequest.getCookies()) - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); - } - - private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { - return Arrays.stream(cookies) - .filter(cookie -> tokenName.equals(cookie.getName())) - .map(Cookie::getValue) - .findFirst() - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); - } + private final HandlerExceptionResolver handlerExceptionResolver; + private final JwtAuthenticationService authenticationService; + private final AuthorizationService authorizationService; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, + @NotNull HttpServletResponse httpServletResponse, @NotNull FilterChain filterChain) throws + ServletException, + IOException { + + if (isPermit(httpServletRequest)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + + try { + invoke(httpServletRequest, httpServletResponse); + } catch (UnauthorizedException unauthorizedException) { + authorizationService.removeToken(httpServletRequest, httpServletResponse); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + private boolean isPermit(HttpServletRequest httpServletRequest) { + Boolean isPermit = (Boolean)httpServletRequest.getAttribute("isPermit"); + + return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); + } + + private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + Cookie[] cookies = getCookiesOrThrow(httpServletRequest); + + if (!isTokenTypeBearer(cookies)) { + throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); + } + + handleTokenAuthenticate(cookies, httpServletResponse, httpServletRequest); + } + + private boolean isTokenTypeBearer(Cookie[] cookies) { + return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); + } + + private void handleTokenAuthenticate(Cookie[] cookies, HttpServletResponse httpServletResponse, + HttpServletRequest httpServletRequest) { + String accessToken = extractTokenFromCookie(cookies, "access_token"); + PublicClaim publicClaim = authenticationService.parseClaim(accessToken); + + if (authenticationService.isTokenExpire(accessToken, publicClaim.role())) { + String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); + + if (authenticationService.isTokenExpire(refreshToken, publicClaim.role())) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + + validInvalidMember(publicClaim, refreshToken, httpServletRequest); + authorizationService.issueServiceToken(httpServletResponse, publicClaim); + } + + AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); + } + + private void validInvalidMember(PublicClaim publicClaim, String refreshToken, + HttpServletRequest httpServletRequest) { + boolean isAdminPath = httpServletRequest.getRequestURI().contains("admins"); + + if (!((publicClaim.role().equals(Role.ADMIN) && isAdminPath) || (publicClaim.role().equals(Role.USER) + && !isAdminPath))) { + throw new BadRequestException(ErrorMessage.INVALID_REQUEST_ROLE); + } + + authorizationService.validTokenPair(publicClaim.id(), refreshToken, publicClaim.role()); + authorizationService.validMemberExist(publicClaim.id(), publicClaim.role()); + } + + private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { + return Optional.ofNullable(httpServletRequest.getCookies()) + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); + } + + private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { + return Arrays.stream(cookies) + .filter(cookie -> tokenName.equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); + } } diff --git a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java index aaafe1b5..70508dbd 100644 --- a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java @@ -1,22 +1,24 @@ package com.moabam.global.auth.filter; +import java.io.IOException; +import java.util.Objects; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + import com.google.cloud.storage.HttpMethod; import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - -import java.io.IOException; -import java.util.Objects; @Slf4j @Order(0) @@ -24,53 +26,50 @@ @RequiredArgsConstructor public class CorsFilter extends OncePerRequestFilter { - private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; - private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " - + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; - - private final HandlerExceptionResolver handlerExceptionResolver; - - private final AllowOriginConfig allowOriginsConfig; - - @Override - protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, - FilterChain filterChain) throws ServletException, IOException { - String refer = httpServletRequest.getHeader("referer"); - String origin = secureMatch(refer); - - try { - if (Objects.isNull(origin)) { - throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); - } - } catch (UnauthorizedException unauthorizedException) { - log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); - httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); - httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); - httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); - httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); - - if (isOption(httpServletRequest.getMethod())) { - httpServletRequest.setAttribute("isPermit", true); - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - public String secureMatch(String refer) { - return allowOriginsConfig.origin().stream() - .filter(refer::contains) - .findFirst() - .orElse(null); - } - - public boolean isOption(String method) { - return HttpMethod.OPTIONS.name().equals(method); - } + private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; + private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " + + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; + + private final HandlerExceptionResolver handlerExceptionResolver; + + private final AllowOriginConfig allowOriginsConfig; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + String refer = httpServletRequest.getHeader("referer"); + String origin = secureMatch(refer); + + try { + if (Objects.isNull(origin)) { + throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); + } + } catch (UnauthorizedException unauthorizedException) { + log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); + httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); + httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); + + if (isOption(httpServletRequest.getMethod())) { + httpServletRequest.setAttribute("isPermit", true); + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + public String secureMatch(String refer) { + return allowOriginsConfig.origin().stream().filter(refer::contains).findFirst().orElse(null); + } + + public boolean isOption(String method) { + return HttpMethod.OPTIONS.name().equals(method); + } } diff --git a/src/main/java/com/moabam/global/config/AllowOriginConfig.java b/src/main/java/com/moabam/global/config/AllowOriginConfig.java index 3392dd90..b580a99f 100644 --- a/src/main/java/com/moabam/global/config/AllowOriginConfig.java +++ b/src/main/java/com/moabam/global/config/AllowOriginConfig.java @@ -1,14 +1,13 @@ package com.moabam.global.config; -import org.springframework.boot.context.properties.ConfigurationProperties; - import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; + @ConfigurationProperties(prefix = "allows") public record AllowOriginConfig( - String adminDomain, - String domain, - List origin -) { + String adminDomain, + String domain, + List origin) { } diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index b0e56d22..a52ba6e9 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -461,7 +461,7 @@

요청

POST /admins/coupons HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 178
+Content-Length: 186
 Host: localhost:8080
 
 {
@@ -540,7 +540,7 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 192 +Content-Length: 202 { "id" : 24, @@ -571,7 +571,7 @@

요청

POST /coupons/search HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 41
+Content-Length: 44
 Host: localhost:8080
 
 {
@@ -590,7 +590,7 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 193 +Content-Length: 203 [ { "id" : 25, @@ -637,7 +637,7 @@

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 63 +Content-Length: 65 { "message" : "이미 쿠폰 발급에 성공했습니다!" @@ -716,7 +716,7 @@

응답

diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 154f140e..4c8f2a12 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -616,7 +616,7 @@

diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html index a6327a68..d4ddae0c 100644 --- a/src/main/resources/static/docs/notification.html +++ b/src/main/resources/static/docs/notification.html @@ -513,7 +513,7 @@

응답

diff --git a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java index 678428ff..b746b6d6 100644 --- a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java @@ -1,12 +1,40 @@ package com.moabam.api.application.auth; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.util.ReflectionTestUtils; + import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.application.member.MemberService; import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.*; +import com.moabam.api.dto.auth.AuthorizationCodeRequest; +import com.moabam.api.dto.auth.AuthorizationCodeResponse; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.AuthorizationTokenRequest; +import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.api.dto.auth.LoginResponse; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; @@ -23,344 +51,323 @@ import com.moabam.support.fixture.AuthorizationResponseFixture; import com.moabam.support.fixture.MemberFixture; import com.moabam.support.fixture.TokenSaveValueFixture; -import jakarta.servlet.http.Cookie; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; +import jakarta.servlet.http.Cookie; @ExtendWith({MockitoExtension.class, FilterProcessExtension.class}) class AuthorizationServiceTest { - @InjectMocks - AuthorizationService authorizationService; - - @Mock - OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; - - @Mock - MemberService memberService; - - @Mock - AdminService adminService; - - @Mock - JwtProviderService jwtProviderService; - - @Mock - FcmService fcmService; - - @Mock - TokenRepository tokenRepository; - - AllowOriginConfig allowOriginsConfig; - OAuthConfig oauthConfig; - TokenConfig tokenConfig; - AuthorizationService noPropertyService; - OAuthConfig noOAuthConfig; - String domain = "Test"; - - @BeforeEach - public void initParams() { - String secretKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; - String adminKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; - - allowOriginsConfig = new AllowOriginConfig(domain, domain, List.of("test", "test")); - ReflectionTestUtils.setField(authorizationService, "allowOriginsConfig", allowOriginsConfig); - tokenConfig = new TokenConfig(null, 100000, 150000, secretKey, adminKey); - ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); - - oauthConfig = new OAuthConfig( - new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", - "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), - new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", - List.of("profile_nickname", "profile_image"), "adminKey")); - ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); - - noOAuthConfig = new OAuthConfig( - new OAuthConfig.Provider(null, null, null, null, null, null), - new OAuthConfig.Client(null, null, null, null, null, null)); - noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, - oAuth2AuthorizationServerRequestService, memberService, adminService, - jwtProviderService, tokenRepository, allowOriginsConfig); - } - - @DisplayName("인가코드 URI 생성 매퍼 실패") - @Test - void authorization_code_request_mapping_fail() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("인가코드 URI 생성 매퍼 성공") - @Test - void authorization_code_request_mapping_success() { - // Given - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); - - // When + Then - assertThat(authorizationCodeRequest).isNotNull(); - assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); - } - - @DisplayName("redirect 로그인페이지 성공") - @Test - void redirect_loginPage_success() { - // given - MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); - - // when - authorizationService.redirectToLoginPage(mockHttpServletResponse); - - // then - verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); - } - - @DisplayName("인가코드 반환 실패") - @Test - void authorization_grant_fail() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", - "errorDescription", null); - - // When + Then - assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( - BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); - } - - @DisplayName("인가코드 반환 성공") - @Test - void authorization_grant_success() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = - new AuthorizationCodeResponse("test", null, null, null); - AuthorizationTokenResponse authorizationTokenResponse = - AuthorizationResponseFixture.authorizationTokenResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( - new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); - } - - @DisplayName("토큰 요청 매퍼 실패 - code null") - @Test - void token_request_mapping_failBy_code() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, - null, oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 실패 - config 에러") - @Test - void token_request_mapping_failBy_config() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", - oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 성공") - @Test - void token_request_mapping_success() { - // Given - String code = "Test"; - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oauthConfig, code, oauthConfig.provider().redirectUri()); - - // When + Then - assertThat(authorizationTokenRequest).isNotNull(); - assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), - () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); - } - - @DisplayName("토큰 변경 성공") - @Test - void generate_token() { - // Given - AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); - AuthorizationTokenInfoResponse tokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), - eq("Bearer " + tokenResponse.accessToken()))).thenReturn( - new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); - - // Then - assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); - } - - @DisplayName("회원 가입 및 로그인 성공 테스트") - @ParameterizedTest - @ValueSource(booleans = {true, false}) - void signUp_success(boolean isSignUp) { - // given - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - AuthorizationTokenInfoResponse authorizationTokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - LoginResponse loginResponse = LoginResponse.builder() - .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").role(Role.USER).build()) - .isSignUp(isSignUp) - .build(); - - willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); - - // when - LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); - - // then - assertThat(loginResponse).isEqualTo(result); - - Cookie tokenType = httpServletResponse.getCookie("token_type"); - assertThat(tokenType).isNotNull(); - assertThat(tokenType.getValue()).isEqualTo("Bearer"); - - Cookie accessCookie = httpServletResponse.getCookie("access_token"); - assertThat(accessCookie).isNotNull(); - assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), - () -> assertThat(accessCookie.isHttpOnly()).isTrue(), - () -> assertThat(accessCookie.getPath()).isEqualTo("/")); - - Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); - assertThat(refreshCookie).isNotNull(); - assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), - () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), - () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); - } - - @DisplayName("토큰 redis 검증") - @Test - void valid_token_in_redis() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")) - .given(tokenRepository).getTokenSaveValue(1L, Role.USER); - - // When + Then - assertThatNoException().isThrownBy(() -> - authorizationService.validTokenPair(1L, "token", Role.USER)); - } - - @DisplayName("이전 토큰과 동일한지 검증") - @Test - void valid_token_failby_notEquals_token() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")) - .given(tokenRepository).getTokenSaveValue(1L, Role.USER); - - // When + Then - assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken", Role.USER)).isInstanceOf( - UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); - verify(tokenRepository).delete(1L, Role.USER); - } - - @DisplayName("토큰 삭제 성공") - @Test - void error_with_expire_token(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - httpServletRequest.setCookies( - CookieUtils.tokenCookie("access_token", "value", 100000, domain), - CookieUtils.tokenCookie("refresh_token", "value", 100000, domain), - CookieUtils.typeCookie("Bearer", 100000, domain)); - - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - Cookie cookie = httpServletResponse.getCookie("access_token"); - - // Then - assertThat(cookie).isNotNull(); - assertThat(cookie.getMaxAge()).isZero(); - assertThat(cookie.getValue()).isEqualTo("value"); - - verify(tokenRepository).delete(authMember.id(), Role.USER); - } - - @DisplayName("토큰 없어서 삭제 실패") - @Test - void token_null_delete_fail(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - - // Then - assertThat(httpServletResponse.getCookies()).isEmpty(); - } - - @DisplayName("회원 탈퇴 요청 성공") - @Test - void unlink_success(@WithMember AuthMember authMember) { - // given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - doNothing().when(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); - } - - @DisplayName("회원이 없어서 찾기 실패") - @Test - void unlink_failBy_find_Member(@WithMember AuthMember authMember) { - // Given + When - willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) - .given(memberService) - .findMemberToDelete(authMember.id()); - - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(NotFoundException.class) - .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); - } - - @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") - @Test - void unlink_failBy_(@WithMember AuthMember authMember) { - // Given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - willThrow(BadRequestException.class) - .given(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(BadRequestException.class) - .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); - } + @InjectMocks + AuthorizationService authorizationService; + + @Mock + OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; + + @Mock + MemberService memberService; + + @Mock + AdminService adminService; + + @Mock + JwtProviderService jwtProviderService; + + @Mock + FcmService fcmService; + + @Mock + TokenRepository tokenRepository; + + AllowOriginConfig allowOriginsConfig; + OAuthConfig oauthConfig; + TokenConfig tokenConfig; + AuthorizationService noPropertyService; + OAuthConfig noOAuthConfig; + String domain = "Test"; + + @BeforeEach + public void initParams() { + String secretKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + String adminKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + + allowOriginsConfig = new AllowOriginConfig(domain, domain, List.of("test", "test")); + ReflectionTestUtils.setField(authorizationService, "allowOriginsConfig", allowOriginsConfig); + tokenConfig = new TokenConfig(null, 100000, 150000, secretKey, adminKey); + ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); + + oauthConfig = new OAuthConfig( + new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", + "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), + new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", + List.of("profile_nickname", "profile_image"), "adminKey")); + ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); + + noOAuthConfig = new OAuthConfig( + new OAuthConfig.Provider(null, null, null, null, null, null), + new OAuthConfig.Client(null, null, null, null, null, null)); + noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, + oAuth2AuthorizationServerRequestService, memberService, adminService, + jwtProviderService, tokenRepository, allowOriginsConfig); + } + + @DisplayName("인가코드 URI 생성 매퍼 실패") + @Test + void authorization_code_request_mapping_fail() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("인가코드 URI 생성 매퍼 성공") + @Test + void authorization_code_request_mapping_success() { + // Given + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); + + // When + Then + assertThat(authorizationCodeRequest).isNotNull(); + assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); + } + + @DisplayName("redirect 로그인페이지 성공") + @Test + void redirect_loginPage_success() { + // given + MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); + + // when + authorizationService.redirectToLoginPage(mockHttpServletResponse); + + // then + verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); + } + + @DisplayName("인가코드 반환 실패") + @Test + void authorization_grant_fail() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", + "errorDescription", null); + + // When + Then + assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( + BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); + } + + @DisplayName("인가코드 반환 성공") + @Test + void authorization_grant_success() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = + new AuthorizationCodeResponse("test", null, null, null); + AuthorizationTokenResponse authorizationTokenResponse = + AuthorizationResponseFixture.authorizationTokenResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( + new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); + } + + @DisplayName("토큰 요청 매퍼 실패 - code null") + @Test + void token_request_mapping_failBy_code() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, + null, oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 실패 - config 에러") + @Test + void token_request_mapping_failBy_config() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", + oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 성공") + @Test + void token_request_mapping_success() { + // Given + String code = "Test"; + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oauthConfig, code, oauthConfig.provider().redirectUri()); + + // When + Then + assertThat(authorizationTokenRequest).isNotNull(); + assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), + () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); + } + + @DisplayName("토큰 변경 성공") + @Test + void generate_token() { + // Given + AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); + AuthorizationTokenInfoResponse tokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), + eq("Bearer " + tokenResponse.accessToken()))).thenReturn( + new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); + + // Then + assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); + } + + @DisplayName("회원 가입 및 로그인 성공 테스트") + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void signUp_success(boolean isSignUp) { + // given + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + LoginResponse loginResponse = LoginResponse.builder() + .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").role(Role.USER).build()) + .isSignUp(isSignUp) + .build(); + + willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); + + // when + LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + + // then + assertThat(loginResponse).isEqualTo(result); + + Cookie tokenType = httpServletResponse.getCookie("token_type"); + assertThat(tokenType).isNotNull(); + assertThat(tokenType.getValue()).isEqualTo("Bearer"); + + Cookie accessCookie = httpServletResponse.getCookie("access_token"); + assertThat(accessCookie).isNotNull(); + assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), + () -> assertThat(accessCookie.isHttpOnly()).isTrue(), + () -> assertThat(accessCookie.getPath()).isEqualTo("/")); + + Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); + assertThat(refreshCookie).isNotNull(); + assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), + () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), + () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); + } + + @DisplayName("토큰 redis 검증") + @Test + void valid_token_in_redis() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatNoException().isThrownBy(() -> + authorizationService.validTokenPair(1L, "token", Role.USER)); + } + + @DisplayName("이전 토큰과 동일한지 검증") + @Test + void valid_token_failby_notEquals_token() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken", Role.USER)).isInstanceOf( + UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); + verify(tokenRepository).delete(1L, Role.USER); + } + + @DisplayName("토큰 삭제 성공") + @Test + void error_with_expire_token(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.setCookies( + CookieUtils.tokenCookie("access_token", "value", 100000, domain), + CookieUtils.tokenCookie("refresh_token", "value", 100000, domain), + CookieUtils.typeCookie("Bearer", 100000, domain)); + + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + Cookie cookie = httpServletResponse.getCookie("access_token"); + + // Then + assertThat(cookie).isNotNull(); + assertThat(cookie.getMaxAge()).isZero(); + assertThat(cookie.getValue()).isEqualTo("value"); + + verify(tokenRepository).delete(authMember.id(), Role.USER); + } + + @DisplayName("토큰 없어서 삭제 실패") + @Test + void token_null_delete_fail(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + + // Then + assertThat(httpServletResponse.getCookies()).isEmpty(); + } + + @DisplayName("회원 탈퇴 요청 성공") + @Test + void unlink_success(@WithMember AuthMember authMember) { + // given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + doNothing().when(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); + } + + @DisplayName("회원이 없어서 찾기 실패") + @Test + void unlink_failBy_find_Member(@WithMember AuthMember authMember) { + // Given + When + willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) + .given(memberService) + .findMemberToDelete(authMember.id()); + + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(NotFoundException.class) + .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); + } + + @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") + @Test + void unlink_failBy_(@WithMember AuthMember authMember) { + // Given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + willThrow(BadRequestException.class) + .given(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); + } } diff --git a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java index 9ef0d24c..6399e91c 100644 --- a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java @@ -1,12 +1,11 @@ package com.moabam.api.presentation; -import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.UpdateRanking; -import com.moabam.support.annotation.WithMember; -import com.moabam.support.common.WithoutFilterSupporter; -import com.moabam.support.fixture.MemberFixture; +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -19,13 +18,13 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.MemberFixture; @Transactional @SpringBootTest @@ -33,57 +32,57 @@ @AutoConfigureRestDocs class RankingControllerTest extends WithoutFilterSupporter { - @Autowired - MockMvc mockMvc; - - @Autowired - MemberRepository memberRepository; - - @Autowired - RedisTemplate redisTemplate; - - @BeforeEach - void init() { - redisTemplate.delete("Ranking"); - } - - @DisplayName("") - @WithMember - @Test - void top_ranking() throws Exception { - // given - List members = new ArrayList<>(); - for (int i = 0; i < 20; i++) { - Member member = MemberFixture.member(String.valueOf(i + 1)); - members.add(member); - - RankingInfo rankingInfo = new RankingInfo((long) (i + 1), member.getNickname(), member.getProfileImage()); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); - } - memberRepository.saveAll(members); - - RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); - RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); - - UpdateRanking myRanking = UpdateRanking.builder() - .score(1L) - .rankingInfo(RankingInfo.builder() - .nickname(members.get(0).getNickname()) - .memberId(members.get(0).getId()) - .image(members.get(0).getProfileImage()).build()) - .build(); - - // when - mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.topRankings", hasSize(10))) - .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) - .andExpect(jsonPath("$.myRanking.rank", is(22))); - - // then - - } + @Autowired + MockMvc mockMvc; + + @Autowired + MemberRepository memberRepository; + + @Autowired + RedisTemplate redisTemplate; + + @BeforeEach + void init() { + redisTemplate.delete("Ranking"); + } + + @DisplayName("") + @WithMember + @Test + void top_ranking() throws Exception { + // given + List members = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + Member member = MemberFixture.member(String.valueOf(i + 1)); + members.add(member); + + RankingInfo rankingInfo = new RankingInfo((long)(i + 1), member.getNickname(), member.getProfileImage()); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); + } + memberRepository.saveAll(members); + + RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); + RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); + + UpdateRanking myRanking = UpdateRanking.builder() + .score(1L) + .rankingInfo(RankingInfo.builder() + .nickname(members.get(0).getNickname()) + .memberId(members.get(0).getId()) + .image(members.get(0).getProfileImage()).build()) + .build(); + + // when + mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topRankings", hasSize(10))) + .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) + .andExpect(jsonPath("$.myRanking.rank", is(22))); + + // then + + } } diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index 011c3514..a58671cb 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -1,9 +1,10 @@ package com.moabam.support.common; -import com.moabam.api.domain.member.Role; -import com.moabam.global.auth.filter.CorsFilter; -import com.moabam.global.auth.handler.PathResolver; -import com.moabam.global.config.AllowOriginConfig; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.mock.mockito.MockBean; @@ -11,36 +12,36 @@ import org.springframework.context.annotation.Import; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willReturn; +import com.moabam.api.domain.member.Role; +import com.moabam.global.auth.filter.CorsFilter; +import com.moabam.global.auth.handler.PathResolver; +import com.moabam.global.config.AllowOriginConfig; @Import(DataCleanResolver.class) @ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) public class WithoutFilterSupporter { - @MockBean - private PathResolver pathResolver; + @MockBean + private PathResolver pathResolver; - @MockBean - private DefaultHandlerExceptionResolver handlerExceptionResolver; + @MockBean + private DefaultHandlerExceptionResolver handlerExceptionResolver; - @SpyBean - private CorsFilter corsFilter; + @SpyBean + private CorsFilter corsFilter; - @MockBean - private AllowOriginConfig allowOriginConfig; + @MockBean + private AllowOriginConfig allowOriginConfig; - @BeforeEach - void setUpMock() { - willReturn("http://localhost:8080") - .given(corsFilter).secureMatch(any()); + @BeforeEach + void setUpMock() { + willReturn("http://localhost:8080") + .given(corsFilter).secureMatch(any()); - willReturn(Optional.of(PathResolver.Path.builder() - .uri("/") - .role(Role.USER) - .build())) - .given(pathResolver).permitPathMatch(any()); - } + willReturn(Optional.of(PathResolver.Path.builder() + .uri("/") + .role(Role.USER) + .build())) + .given(pathResolver).permitPathMatch(any()); + } } diff --git a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java index 3c4d35d0..6f43c132 100644 --- a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java +++ b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java @@ -1,40 +1,40 @@ package com.moabam.support.fixture; -import com.moabam.api.domain.member.BadgeType; -import com.moabam.api.dto.member.MemberInfo; +import static com.moabam.global.common.util.BaseImageUrl.*; import java.util.List; -import static com.moabam.global.common.util.BaseImageUrl.*; +import com.moabam.api.domain.member.BadgeType; +import com.moabam.api.dto.member.MemberInfo; public class MemberInfoSearchFixture { - private static final String NICKNAME = "nickname"; - private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; - private static final String INTRO = "intro"; - private static final long TOTAL_CERTIFY_COUNT = 15; - private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; - private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; - - public static List friendMemberInfo() { - return friendMemberInfo(TOTAL_CERTIFY_COUNT); - } - - public static List friendMemberInfo(long total) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.BIRTH, - 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.LEVEL10, - 0, 0, 0) - ); - } - - public static List myInfo(String morningImage, String nightImage) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.BIRTH, 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.LEVEL10, 0, 0, 0) - ); - } + private static final String NICKNAME = "nickname"; + private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; + private static final String INTRO = "intro"; + private static final long TOTAL_CERTIFY_COUNT = 15; + private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; + private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; + + public static List friendMemberInfo() { + return friendMemberInfo(TOTAL_CERTIFY_COUNT); + } + + public static List friendMemberInfo(long total) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.BIRTH, + 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.LEVEL10, + 0, 0, 0) + ); + } + + public static List myInfo(String morningImage, String nightImage) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.BIRTH, 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.LEVEL10, 0, 0, 0) + ); + } }