From da36a04f303cba74904c20197f006e49a51829fe Mon Sep 17 00:00:00 2001 From: Park Seyeon Date: Mon, 27 Nov 2023 15:58:08 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B0=A9/=ED=9A=8C=EC=9B=90/=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=A0=EA=B3=A0=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 삭제된 회원 조회 테스트 추가 * refactor: 회원 조회 변경 * feat: 신고 기능 추가 및 테스트 코드 추가 * refactor: 신고 기능 로직 수정 및 테스트 코드 추가 * feat: 신고 api 기능 추가 및 테스트 코드 추가 * fix: 통합 테스트간 데이터 중복 및 index 문제 해결 * refactor: CsvSource null 부분 변경 --- .../api/application/bug/BugService.java | 4 +- .../api/application/item/ItemService.java | 2 +- .../api/application/member/MemberService.java | 4 +- .../api/application/report/ReportMapper.java | 23 +++ .../api/application/report/ReportService.java | 60 +++++++ .../room/CertificationService.java | 7 +- .../api/application/room/RoomService.java | 17 +- .../com/moabam/api/domain/report/Report.java | 56 ++++++ .../report/repository/ReportRepository.java | 9 + .../moabam/api/dto/report/ReportRequest.java | 12 ++ .../api/presentation/ReportController.java | 30 ++++ .../global/error/model/ErrorMessage.java | 3 + src/main/resources/config | 2 +- .../api/application/bug/BugServiceTest.java | 2 +- .../api/application/item/ItemServiceTest.java | 4 +- .../application/report/ReportServiceTest.java | 93 ++++++++++ .../room/CertificationServiceTest.java | 2 +- .../api/application/room/RoomServiceTest.java | 6 +- .../domain/member/MemberRepositoryTest.java | 38 ++++ .../api/presentation/BugControllerTest.java | 2 +- .../api/presentation/ItemControllerTest.java | 2 +- .../presentation/MemberControllerTest.java | 1 - .../presentation/PaymentControllerTest.java | 2 +- .../presentation/ReportControllerTest.java | 162 ++++++++++++++++++ .../support/common/ClearDataExtension.java | 15 ++ .../support/common/DataCleanResolver.java | 54 ++++++ .../common/WithoutFilterSupporter.java | 8 +- .../moabam/support/fixture/ReportFixture.java | 30 ++++ 28 files changed, 623 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/moabam/api/application/report/ReportMapper.java create mode 100644 src/main/java/com/moabam/api/application/report/ReportService.java create mode 100644 src/main/java/com/moabam/api/domain/report/Report.java create mode 100644 src/main/java/com/moabam/api/domain/report/repository/ReportRepository.java create mode 100644 src/main/java/com/moabam/api/dto/report/ReportRequest.java create mode 100644 src/main/java/com/moabam/api/presentation/ReportController.java create mode 100644 src/test/java/com/moabam/api/application/report/ReportServiceTest.java create mode 100644 src/test/java/com/moabam/api/presentation/ReportControllerTest.java create mode 100644 src/test/java/com/moabam/support/common/ClearDataExtension.java create mode 100644 src/test/java/com/moabam/support/common/DataCleanResolver.java create mode 100644 src/test/java/com/moabam/support/fixture/ReportFixture.java diff --git a/src/main/java/com/moabam/api/application/bug/BugService.java b/src/main/java/com/moabam/api/application/bug/BugService.java index 3e6c1295..d35e7549 100644 --- a/src/main/java/com/moabam/api/application/bug/BugService.java +++ b/src/main/java/com/moabam/api/application/bug/BugService.java @@ -44,7 +44,7 @@ public class BugService { private final PaymentRepository paymentRepository; public BugResponse getBug(Long memberId) { - Bug bug = memberService.getById(memberId).getBug(); + Bug bug = memberService.findMember(memberId).getBug(); return BugMapper.toBugResponse(bug); } @@ -77,7 +77,7 @@ public PurchaseProductResponse purchaseBugProduct(Long memberId, Long productId, @Transactional public void charge(Long memberId, Product bugProduct) { - Bug bug = memberService.getById(memberId).getBug(); + Bug bug = memberService.findMember(memberId).getBug(); bug.charge(bugProduct.getQuantity()); bugHistoryRepository.save(BugMapper.toChargeBugHistory(memberId, bugProduct.getQuantity())); diff --git a/src/main/java/com/moabam/api/application/item/ItemService.java b/src/main/java/com/moabam/api/application/item/ItemService.java index 3a024c2e..a0d5015f 100644 --- a/src/main/java/com/moabam/api/application/item/ItemService.java +++ b/src/main/java/com/moabam/api/application/item/ItemService.java @@ -49,7 +49,7 @@ public ItemsResponse getItems(Long memberId, ItemType type) { @Transactional public void purchaseItem(Long memberId, Long itemId, PurchaseItemRequest request) { Item item = getItem(itemId); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); validateAlreadyPurchased(memberId, itemId); item.validatePurchasable(request.bugType(), member.getLevel()); 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 ce4ca41a..cbc5f451 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -41,8 +41,8 @@ public class MemberService { private final MemberSearchRepository memberSearchRepository; private final ClockHolder clockHolder; - public Member getById(Long memberId) { - return memberRepository.findById(memberId) + public Member findMember(Long memberId) { + return memberSearchRepository.findMember(memberId) .orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND)); } diff --git a/src/main/java/com/moabam/api/application/report/ReportMapper.java b/src/main/java/com/moabam/api/application/report/ReportMapper.java new file mode 100644 index 00000000..7cada261 --- /dev/null +++ b/src/main/java/com/moabam/api/application/report/ReportMapper.java @@ -0,0 +1,23 @@ +package com.moabam.api.application.report; + +import com.moabam.api.domain.report.Report; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.Room; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReportMapper { + + public static Report toReport(Long reporterId, Long reportedMemberId, + Room room, Certification certification, String description) { + return Report.builder() + .reporterId(reporterId) + .reportedMemberId(reportedMemberId) + .certification(certification) + .room(room) + .description(description) + .build(); + } +} diff --git a/src/main/java/com/moabam/api/application/report/ReportService.java b/src/main/java/com/moabam/api/application/report/ReportService.java new file mode 100644 index 00000000..f626383d --- /dev/null +++ b/src/main/java/com/moabam/api/application/report/ReportService.java @@ -0,0 +1,60 @@ +package com.moabam.api.application.report; + +import static java.util.Objects.*; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.moabam.api.application.member.MemberService; +import com.moabam.api.application.room.CertificationService; +import com.moabam.api.application.room.RoomService; +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.report.Report; +import com.moabam.api.domain.report.repository.ReportRepository; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.Room; +import com.moabam.api.dto.report.ReportRequest; +import com.moabam.global.auth.model.AuthMember; +import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.model.ErrorMessage; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ReportService { + + private final MemberService memberService; + private final RoomService roomService; + private final CertificationService certificationService; + private final ReportRepository reportRepository; + + @Transactional + public void report(AuthMember authMember, ReportRequest reportRequest) { + validateNoReportSubject(reportRequest.roomId(), reportRequest.certificationId()); + Report report = createReport(authMember.id(), reportRequest); + reportRepository.save(report); + } + + private Report createReport(Long reporterId, ReportRequest reportRequest) { + Member reportedMember = memberService.findMember(reportRequest.reportedId()); + + if (nonNull(reportRequest.certificationId())) { + Certification certification = certificationService.findCertification(reportRequest.certificationId()); + + return ReportMapper.toReport(reporterId, reportedMember.getId(), + null, certification, reportRequest.description()); + } + + Room room = roomService.findRoom(reportRequest.roomId()); + + return ReportMapper.toReport(reporterId, reportedMember.getId(), + room, null, reportRequest.description()); + } + + private void validateNoReportSubject(Long roomId, Long certificationId) { + if (isNull(roomId) && isNull(certificationId)) { + throw new BadRequestException(ErrorMessage.REPORT_REQUEST_ERROR); + } + } +} 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 3b40a8f0..f8e72e9d 100644 --- a/src/main/java/com/moabam/api/application/room/CertificationService.java +++ b/src/main/java/com/moabam/api/application/room/CertificationService.java @@ -59,7 +59,7 @@ public CertifiedMemberInfo getCertifiedMemberInfo(Long memberId, Long roomId, Li Participant participant = participantSearchRepository.findOne(memberId, roomId) .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); Room room = participant.getRoom(); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); BugType bugType = switch (room.getRoomType()) { case MORNING -> BugType.MORNING; case NIGHT -> BugType.NIGHT; @@ -100,6 +100,11 @@ public boolean existsRoomCertification(Long roomId, LocalDate date) { return dailyRoomCertificationRepository.existsByRoomIdAndCertifiedAt(roomId, date); } + public Certification findCertification(Long certificationId) { + return certificationRepository.findById(certificationId) + .orElseThrow(() -> new NotFoundException(CERTIFICATION_NOT_FOUND)); + } + private void validateCertifyTime(LocalDateTime now, int certifyTime) { LocalTime targetTime = LocalTime.of(certifyTime, 0); LocalDateTime minusTenMinutes = LocalDateTime.of(now.toLocalDate(), targetTime).minusMinutes(10); 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 fab05eed..d50c4658 100644 --- a/src/main/java/com/moabam/api/application/room/RoomService.java +++ b/src/main/java/com/moabam/api/application/room/RoomService.java @@ -52,7 +52,7 @@ public Long createRoom(Long memberId, String nickname, CreateRoomRequest createR validateEnteredRoomCount(memberId, room.getRoomType()); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); member.enterRoom(room.getRoomType()); participant.enableManager(); room.changeManagerNickname(nickname); @@ -89,7 +89,7 @@ public void enterRoom(Long memberId, Long roomId, EnterRoomRequest enterRoomRequ () -> new NotFoundException(ROOM_NOT_FOUND)); validateRoomEnter(memberId, enterRoomRequest.password(), room); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); member.enterRoom(room.getRoomType()); room.increaseCurrentUserCount(); @@ -104,7 +104,7 @@ public void exitRoom(Long memberId, Long roomId) { validateRoomExit(participant, room); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); member.exitRoom(room.getRoomType()); participant.removeRoom(); @@ -126,7 +126,7 @@ public void mandateManager(Long managerId, Long roomId, Long memberId) { validateManagerAuthorization(managerParticipant); Room room = managerParticipant.getRoom(); - Member member = memberService.getById(memberParticipant.getMemberId()); + Member member = memberService.findMember(memberParticipant.getMemberId()); room.changeManagerNickname(member.getNickname()); managerParticipant.disableManager(); @@ -143,7 +143,7 @@ public void deportParticipant(Long managerId, Long roomId, Long memberId) { participantRepository.delete(memberParticipant); room.decreaseCurrentUserCount(); - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); member.exitRoom(room.getRoomType()); } @@ -153,6 +153,11 @@ public void validateRoomById(Long roomId) { } } + public Room findRoom(Long roomId) { + return roomRepository.findById(roomId) + .orElseThrow(() -> new NotFoundException(ROOM_NOT_FOUND)); + } + private Participant getParticipant(Long memberId, Long roomId) { return participantSearchRepository.findOne(memberId, roomId) .orElseThrow(() -> new NotFoundException(PARTICIPANT_NOT_FOUND)); @@ -176,7 +181,7 @@ private void validateRoomEnter(Long memberId, String requestPassword, Room room) } private void validateEnteredRoomCount(Long memberId, RoomType roomType) { - Member member = memberService.getById(memberId); + Member member = memberService.findMember(memberId); if (roomType.equals(MORNING) && member.getCurrentMorningCount() >= 3) { throw new BadRequestException(MEMBER_ROOM_EXCEED); diff --git a/src/main/java/com/moabam/api/domain/report/Report.java b/src/main/java/com/moabam/api/domain/report/Report.java new file mode 100644 index 00000000..4e120055 --- /dev/null +++ b/src/main/java/com/moabam/api/domain/report/Report.java @@ -0,0 +1,56 @@ +package com.moabam.api.domain.report; + +import static java.util.Objects.*; + +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.Room; +import com.moabam.global.common.entity.BaseTimeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "report") +@Entity +public class Report extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "reporter_id", nullable = false, updatable = false) + private Long reporterId; + + @Column(name = "reported_member_id", nullable = false, updatable = false) + private Long reportedMemberId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id", updatable = false) + private Room room; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "certification_id", updatable = false) + private Certification certification; + + @Column(name = "description") + private String description; + + @Builder + private Report(Long reporterId, Long reportedMemberId, Room room, Certification certification, String description) { + this.reporterId = requireNonNull(reporterId); + this.reportedMemberId = requireNonNull(reportedMemberId); + this.room = room; + this.certification = certification; + this.description = description; + } +} diff --git a/src/main/java/com/moabam/api/domain/report/repository/ReportRepository.java b/src/main/java/com/moabam/api/domain/report/repository/ReportRepository.java new file mode 100644 index 00000000..1655fbbc --- /dev/null +++ b/src/main/java/com/moabam/api/domain/report/repository/ReportRepository.java @@ -0,0 +1,9 @@ +package com.moabam.api.domain.report.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.moabam.api.domain.report.Report; + +public interface ReportRepository extends JpaRepository { + +} diff --git a/src/main/java/com/moabam/api/dto/report/ReportRequest.java b/src/main/java/com/moabam/api/dto/report/ReportRequest.java new file mode 100644 index 00000000..fdccf2e3 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/report/ReportRequest.java @@ -0,0 +1,12 @@ +package com.moabam.api.dto.report; + +import jakarta.validation.constraints.NotNull; + +public record ReportRequest( + @NotNull Long reportedId, + Long roomId, + Long certificationId, + String description +) { + +} diff --git a/src/main/java/com/moabam/api/presentation/ReportController.java b/src/main/java/com/moabam/api/presentation/ReportController.java new file mode 100644 index 00000000..cd23d3fd --- /dev/null +++ b/src/main/java/com/moabam/api/presentation/ReportController.java @@ -0,0 +1,30 @@ +package com.moabam.api.presentation; + +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.api.application.report.ReportService; +import com.moabam.api.dto.report.ReportRequest; +import com.moabam.global.auth.annotation.Auth; +import com.moabam.global.auth.model.AuthMember; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/reports") +@RequiredArgsConstructor +public class ReportController { + + private final ReportService reportService; + + @PostMapping + @ResponseStatus(HttpStatus.OK) + public void reports(@Auth AuthMember authMember, @Valid @RequestBody ReportRequest reportRequest) { + reportService.report(authMember, reportRequest); + } +} 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 3cfa68f2..078d7fba 100644 --- a/src/main/java/com/moabam/global/error/model/ErrorMessage.java +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -13,6 +13,8 @@ public enum ErrorMessage { NOT_FOUND_AVAILABLE_PORT("사용 가능한 포트를 찾을 수 없습니다. (10000 ~ 65535)"), ERROR_EXECUTING_EMBEDDED_REDIS("Embedded Redis 실행 중 오류가 발생했습니다."), + REPORT_REQUEST_ERROR("신고 요청하고자 하는 방이나 대상이 존재하지 않습니다."), + ROOM_NOT_FOUND("존재하지 않는 방 입니다."), ROOM_MAX_USER_COUNT_MODIFY_FAIL("잘못된 최대 인원수 설정입니다."), ROOM_MODIFY_UNAUTHORIZED_REQUEST("방장이 아닌 사용자는 방을 수정할 수 없습니다."), @@ -26,6 +28,7 @@ public enum ErrorMessage { ROUTINE_NOT_FOUND("루틴을 찾을 수 없습니다"), INVALID_REQUEST_URL("잘못된 URL 요청입니다."), INVALID_CERTIFY_TIME("현재 인증 시간이 아닙니다."), + CERTIFICATION_NOT_FOUND("인증 정보가 없습니다."), LOGIN_FAILED("로그인에 실패했습니다."), REQUEST_FAILED("네트워크 접근 실패입니다."), diff --git a/src/main/resources/config b/src/main/resources/config index ea25d857..2e460460 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit ea25d85744c2e6fcedbdb66b34c08837d382814d +Subproject commit 2e460460a0048796a3ef6fef17697935027a8708 diff --git a/src/test/java/com/moabam/api/application/bug/BugServiceTest.java b/src/test/java/com/moabam/api/application/bug/BugServiceTest.java index 1cde9be1..fa075dc6 100644 --- a/src/test/java/com/moabam/api/application/bug/BugServiceTest.java +++ b/src/test/java/com/moabam/api/application/bug/BugServiceTest.java @@ -59,7 +59,7 @@ void get_bug_success() { // given Long memberId = 1L; Member member = member(); - given(memberService.getById(memberId)).willReturn(member); + given(memberService.findMember(memberId)).willReturn(member); // when BugResponse response = bugService.getBug(memberId); diff --git a/src/test/java/com/moabam/api/application/item/ItemServiceTest.java b/src/test/java/com/moabam/api/application/item/ItemServiceTest.java index c0f06cab..6abe441d 100644 --- a/src/test/java/com/moabam/api/application/item/ItemServiceTest.java +++ b/src/test/java/com/moabam/api/application/item/ItemServiceTest.java @@ -98,7 +98,7 @@ void success() { PurchaseItemRequest request = new PurchaseItemRequest(BugType.GOLDEN); Member member = member(); Item item = nightMageSkin(); - given(memberService.getById(memberId)).willReturn(member); + given(memberService.findMember(memberId)).willReturn(member); given(itemRepository.findById(itemId)).willReturn(Optional.of(item)); given(inventorySearchRepository.findOne(memberId, itemId)).willReturn(Optional.empty()); @@ -106,7 +106,7 @@ void success() { itemService.purchaseItem(memberId, itemId, request); // Then - verify(memberService).getById(memberId); + verify(memberService).findMember(memberId); verify(itemRepository).findById(itemId); verify(inventorySearchRepository).findOne(memberId, itemId); verify(inventoryRepository).save(any(Inventory.class)); diff --git a/src/test/java/com/moabam/api/application/report/ReportServiceTest.java b/src/test/java/com/moabam/api/application/report/ReportServiceTest.java new file mode 100644 index 00000000..25b56d96 --- /dev/null +++ b/src/test/java/com/moabam/api/application/report/ReportServiceTest.java @@ -0,0 +1,93 @@ +package com.moabam.api.application.report; + +import static com.moabam.global.error.model.ErrorMessage.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +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.CsvSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.moabam.api.application.member.MemberService; +import com.moabam.api.application.room.CertificationService; +import com.moabam.api.application.room.RoomService; +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.report.repository.ReportRepository; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.Room; +import com.moabam.api.domain.room.Routine; +import com.moabam.api.dto.report.ReportRequest; +import com.moabam.global.auth.model.AuthMember; +import com.moabam.global.error.exception.BadRequestException; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.FilterProcessExtension; +import com.moabam.support.fixture.MemberFixture; +import com.moabam.support.fixture.ReportFixture; +import com.moabam.support.fixture.RoomFixture; + +@ExtendWith({MockitoExtension.class, FilterProcessExtension.class}) +class ReportServiceTest { + + @InjectMocks + ReportService reportService; + + @Mock + CertificationService certificationService; + + @Mock + RoomService roomService; + + @Mock + MemberService memberService; + + @Mock + ReportRepository reportRepository; + + @DisplayName("신고 대상이 없어서 실패") + @Test + void no_report_subject_fail(@WithMember AuthMember authMember) { + // given + ReportRequest reportRequest = new ReportRequest(5L, null, null, "st"); + + // When + Then + assertThatThrownBy(() -> reportService.report(authMember, reportRequest)) + .isInstanceOf(BadRequestException.class) + .hasMessage(REPORT_REQUEST_ERROR.getMessage()); + } + + @DisplayName("신고 성공") + @ParameterizedTest + @CsvSource({"true, false", "false, true"}) + void report_success(boolean roomFilter, boolean certificationFilter, @WithMember AuthMember authMember) { + // given + Room room = RoomFixture.room(); + Routine routine = RoomFixture.routine(room, "ets"); + Certification certification = RoomFixture.certification(routine); + Member member = spy(MemberFixture.member()); + + Long roomId = null; + Long certificationId = null; + + if (roomFilter) { + given(roomService.findRoom(any())).willReturn(RoomFixture.room()); + roomId = 1L; + } + if (certificationFilter) { + given(certificationService.findCertification(any())).willReturn(certification); + certificationId = 1L; + } + + ReportRequest reportRequest = ReportFixture.reportRequest(2L, roomId, certificationId); + given(member.getId()).willReturn(2L); + given(memberService.findMember(reportRequest.reportedId())).willReturn(member); + + // When + Then + assertThatNoException() + .isThrownBy(() -> reportService.report(authMember, reportRequest)); + } +} 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 7654f782..83c98f41 100644 --- a/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java +++ b/src/test/java/com/moabam/api/application/room/CertificationServiceTest.java @@ -126,7 +126,7 @@ void get_certified_member_info_success() { given(clockHolder.times()).willReturn(LocalDateTime.now().withHour(9).withMinute(58)); given(clockHolder.date()).willReturn(today); given(participantSearchRepository.findOne(memberId, roomId)).willReturn(Optional.of(participant)); - given(memberService.getById(memberId)).willReturn(member1); + given(memberService.findMember(memberId)).willReturn(member1); given(routineRepository.findById(1L)).willReturn(Optional.of(routines.get(0))); given(routineRepository.findById(2L)).willReturn(Optional.of(routines.get(1))); given(dailyMemberCertificationRepository.save(any(DailyMemberCertification.class))).willReturn( 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 b0038193..c2921883 100644 --- a/src/test/java/com/moabam/api/application/room/RoomServiceTest.java +++ b/src/test/java/com/moabam/api/application/room/RoomServiceTest.java @@ -67,7 +67,7 @@ void create_room_no_password_success() { Room expectedRoom = RoomMapper.toRoomEntity(createRoomRequest); given(roomRepository.save(any(Room.class))).willReturn(expectedRoom); - given(memberService.getById(1L)).willReturn(member); + given(memberService.findMember(1L)).willReturn(member); // when Long result = roomService.createRoom(1L, "닉네임", createRoomRequest); @@ -95,7 +95,7 @@ void create_room_with_password_success() { Room expectedRoom = RoomMapper.toRoomEntity(createRoomRequest); given(roomRepository.save(any(Room.class))).willReturn(expectedRoom); - given(memberService.getById(1L)).willReturn(member); + given(memberService.findMember(1L)).willReturn(member); // when Long result = roomService.createRoom(1L, "닉네임", createRoomRequest); @@ -128,7 +128,7 @@ void room_manager_mandate_success() { Optional.of(memberParticipant)); given(participantSearchRepository.findOne(managerId, room.getId())).willReturn( Optional.of(managerParticipant)); - given(memberService.getById(2L)).willReturn(member); + given(memberService.findMember(2L)).willReturn(member); // when roomService.mandateManager(managerId, room.getId(), memberId); 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 b21530fc..8f600444 100644 --- a/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java +++ b/src/test/java/com/moabam/api/domain/member/MemberRepositoryTest.java @@ -2,13 +2,16 @@ import static org.assertj.core.api.Assertions.*; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; +import 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.transaction.annotation.Transactional; import com.moabam.api.application.member.MemberMapper; import com.moabam.api.domain.member.repository.BadgeRepository; @@ -27,6 +30,9 @@ import com.moabam.support.fixture.ParticipantFixture; import com.moabam.support.fixture.RoomFixture; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + @QuerydslRepositoryTest class MemberRepositoryTest { @@ -45,6 +51,9 @@ class MemberRepositoryTest { @Autowired ParticipantRepository participantRepository; + @PersistenceContext + EntityManager entityManager; + @DisplayName("회원 생성 테스트") @Test void test() { @@ -163,4 +172,33 @@ void no_badges_search_success() { assertThat(memberInfoSearchResponse.badges()).isEmpty(); } } + + @DisplayName("삭제된 회원 찾기 테스트") + @Transactional + @Test + void findMemberTest() { + // Given + Member member = MemberFixture.member(); + + // When + memberRepository.save(member); + + member.delete(LocalDateTime.now()); + memberRepository.flush(); + memberRepository.delete(member); + + memberRepository.flush(); + + // then + Optional deletedMember = memberSearchRepository.findMember(member.getId(), false); + + Assertions.assertAll( + () -> assertThat(deletedMember).isPresent(), + () -> { + Member delete = deletedMember.get(); + assertThat(delete.getSocialId()).contains("delete"); + assertThat(delete.getDeletedAt()).isNotNull(); + } + ); + } } diff --git a/src/test/java/com/moabam/api/presentation/BugControllerTest.java b/src/test/java/com/moabam/api/presentation/BugControllerTest.java index 0a8dcb3e..c7887beb 100644 --- a/src/test/java/com/moabam/api/presentation/BugControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/BugControllerTest.java @@ -78,7 +78,7 @@ void get_bug_success() throws Exception { // given Long memberId = getAuthMember().id(); BugResponse expected = BugMapper.toBugResponse(bug()); - given(memberService.getById(memberId)).willReturn(member()); + given(memberService.findMember(memberId)).willReturn(member()); // expected String content = mockMvc.perform(get("/bugs") diff --git a/src/test/java/com/moabam/api/presentation/ItemControllerTest.java b/src/test/java/com/moabam/api/presentation/ItemControllerTest.java index 87168c4a..b637e8f7 100644 --- a/src/test/java/com/moabam/api/presentation/ItemControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/ItemControllerTest.java @@ -114,7 +114,7 @@ void success() throws Exception { Long memberId = getAuthMember().id(); Item item = itemRepository.save(nightMageSkin()); PurchaseItemRequest request = new PurchaseItemRequest(BugType.NIGHT); - given(memberService.getById(memberId)).willReturn(member()); + given(memberService.findMember(memberId)).willReturn(member()); // expected mockMvc.perform(post("/items/{itemId}/purchase", item.getId()) diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 73e43428..4d5cd0ab 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -426,7 +426,6 @@ void search_member_failBy_default_skin_size() throws Exception { // expected mockMvc.perform(get("/members/{memberId}", 123L)) .andExpect(status().is4xxClientError()); - } @DisplayName("회원 정보 요청 성공") diff --git a/src/test/java/com/moabam/api/presentation/PaymentControllerTest.java b/src/test/java/com/moabam/api/presentation/PaymentControllerTest.java index ee2080b9..1fba2dca 100644 --- a/src/test/java/com/moabam/api/presentation/PaymentControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/PaymentControllerTest.java @@ -117,7 +117,7 @@ void success() throws Exception { payment.request(ORDER_ID); ConfirmPaymentRequest request = confirmPaymentRequest(); given(tossPaymentService.confirm(confirmTossPaymentRequest())).willReturn(confirmTossPaymentResponse()); - given(memberService.getById(memberId)).willReturn(member()); + given(memberService.findMember(memberId)).willReturn(member()); // expected mockMvc.perform(post("/payments/confirm") diff --git a/src/test/java/com/moabam/api/presentation/ReportControllerTest.java b/src/test/java/com/moabam/api/presentation/ReportControllerTest.java new file mode 100644 index 00000000..fd724c07 --- /dev/null +++ b/src/test/java/com/moabam/api/presentation/ReportControllerTest.java @@ -0,0 +1,162 @@ +package com.moabam.api.presentation; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDateTime; + +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.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.databind.ObjectMapper; +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.Room; +import com.moabam.api.domain.room.Routine; +import com.moabam.api.domain.room.repository.CertificationRepository; +import com.moabam.api.domain.room.repository.RoomRepository; +import com.moabam.api.domain.room.repository.RoutineRepository; +import com.moabam.api.dto.report.ReportRequest; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.MemberFixture; +import com.moabam.support.fixture.ReportFixture; +import com.moabam.support.fixture.RoomFixture; + +import jakarta.persistence.EntityManagerFactory; + +@Transactional +@SpringBootTest +@AutoConfigureMockMvc +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class ReportControllerTest extends WithoutFilterSupporter { + + @Autowired + MockMvc mockMvc; + + @Autowired + ObjectMapper objectMapper; + + @Autowired + MemberRepository memberRepository; + + @Autowired + RoomRepository roomRepository; + + @Autowired + CertificationRepository certificationRepository; + + @Autowired + RoutineRepository routineRepository; + + @Autowired + EntityManagerFactory entityManagerFactory; + + Member reportedMember; + + @BeforeAll + void setUp() { + reportedMember = MemberFixture.member(); + memberRepository.save(reportedMember); + } + + @DisplayName("방이나 인증 하나 신고") + @WithMember + @ParameterizedTest + @CsvSource({"true, false", "false, true", "true, true"}) + void reports_success(boolean roomFilter, boolean certificationFilter) throws Exception { + // given + String content = "내용"; + Room room = RoomFixture.room(); + Routine routine = RoomFixture.routine(room, content); + Certification certification = RoomFixture.certification(routine); + roomRepository.save(room); + routineRepository.save(routine); + certificationRepository.save(certification); + + Long roomId = null; + Long certificationId = null; + + if (roomFilter) { + roomId = room.getId(); + } + if (certificationFilter) { + certificationId = certification.getId(); + } + + ReportRequest reportRequest = ReportFixture.reportRequest(reportedMember.getId(), roomId, certificationId); + String request = objectMapper.writeValueAsString(reportRequest); + + // expected + mockMvc.perform(post("/reports") + .contentType(MediaType.APPLICATION_JSON) + .content(request)) + .andExpect(status().is2xxSuccessful()); + } + + @DisplayName("방과 인증 값 둘 다 들어오지 않는다면 테스트 실패") + @WithMember + @Test + void reports_failBy_subject_null() throws Exception { + // given + ReportRequest reportRequest = ReportFixture.reportRequest(123L, null, null); + String request = objectMapper.writeValueAsString(reportRequest); + + // expected + mockMvc.perform(post("/reports") + .contentType(MediaType.APPLICATION_JSON) + .content(request)) + .andExpect(status().isBadRequest()); + } + + @DisplayName("회원 조회 실패로 신고 실패") + @WithMember + @Test + void reports_failBy_member() throws Exception { + // given + Member newMember = MemberFixture.member("9999", "n"); + memberRepository.save(newMember); + + newMember.delete(LocalDateTime.now()); + memberRepository.flush(); + memberRepository.delete(newMember); + memberRepository.flush(); + + ReportRequest reportRequest = ReportFixture.reportRequest(newMember.getId(), 1L, 1L); + String request = objectMapper.writeValueAsString(reportRequest); + + // expected + mockMvc.perform(post("/reports") + .contentType(MediaType.APPLICATION_JSON) + .content(request)) + .andExpect(status().is4xxClientError()); + } + + @DisplayName("방이나 인증 하나 신고 실패") + @WithMember + @ParameterizedTest + @CsvSource({"12394,", ",123415", "12394, 123415"}) + void reports_failBy_room_certification(Long roomId, Long certificationId) throws Exception { + // given + ReportRequest reportRequest = ReportFixture.reportRequest(reportedMember.getId(), roomId, + certificationId); + String request = objectMapper.writeValueAsString(reportRequest); + + // expected + mockMvc.perform(post("/reports") + .contentType(MediaType.APPLICATION_JSON) + .content(request)) + .andExpect(status().is4xxClientError()); + } +} diff --git a/src/test/java/com/moabam/support/common/ClearDataExtension.java b/src/test/java/com/moabam/support/common/ClearDataExtension.java new file mode 100644 index 00000000..200a2502 --- /dev/null +++ b/src/test/java/com/moabam/support/common/ClearDataExtension.java @@ -0,0 +1,15 @@ +package com.moabam.support.common; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +public class ClearDataExtension implements AfterAllCallback { + + @Override + public void afterAll(ExtensionContext context) { + DataCleanResolver dataCleanResolver = + SpringExtension.getApplicationContext(context).getBean(DataCleanResolver.class); + dataCleanResolver.clean(); + } +} diff --git a/src/test/java/com/moabam/support/common/DataCleanResolver.java b/src/test/java/com/moabam/support/common/DataCleanResolver.java new file mode 100644 index 00000000..dfd259f0 --- /dev/null +++ b/src/test/java/com/moabam/support/common/DataCleanResolver.java @@ -0,0 +1,54 @@ +package com.moabam.support.common; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.springframework.boot.test.context.TestComponent; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +@TestComponent +public class DataCleanResolver { + + private EntityManager entityManager; + + public DataCleanResolver(@Nullable EntityManager entityManager) { + this.entityManager = entityManager; + } + + @Transactional + public void clean() { + if (entityManager == null) { + return; + } + + List tableInfos = getTableInfos(); + doClean(tableInfos); + entityManager.clear(); + } + + private List getTableInfos() { + List tableInfos = entityManager.createNativeQuery("show tables").getResultList(); + + return tableInfos.stream() + .map(tableInfo -> (String)tableInfo) + .toList(); + } + + private void doClean(List tableInfos) { + setForeignKeyCheck(0); + tableInfos.stream() + .map(tableInfo -> entityManager.createNativeQuery( + String.format("TRUNCATE TABLE %s", tableInfo))) + .forEach(Query::executeUpdate); + setForeignKeyCheck(1); + } + + private void setForeignKeyCheck(int data) { + entityManager.createNativeQuery(String.format("SET foreign_key_checks = %d", data)) + .executeUpdate(); + } +} diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index ae2b827a..a381738f 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -1,7 +1,7 @@ package com.moabam.support.common; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willReturn; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; import java.util.Optional; @@ -9,13 +9,15 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; +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; -@ExtendWith({FilterProcessExtension.class}) +@Import(DataCleanResolver.class) +@ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) public class WithoutFilterSupporter { @MockBean diff --git a/src/test/java/com/moabam/support/fixture/ReportFixture.java b/src/test/java/com/moabam/support/fixture/ReportFixture.java new file mode 100644 index 00000000..a3d48301 --- /dev/null +++ b/src/test/java/com/moabam/support/fixture/ReportFixture.java @@ -0,0 +1,30 @@ +package com.moabam.support.fixture; + +import com.moabam.api.domain.report.Report; +import com.moabam.api.domain.room.Certification; +import com.moabam.api.domain.room.Room; +import com.moabam.api.dto.report.ReportRequest; + +public class ReportFixture { + + private static Long reportedId = 99L; + private static Long roomId = 1L; + private static Long certificationId = 1L; + + public static Report report(Room room, Certification certification) { + return Report.builder() + .reporterId(1L) + .reportedMemberId(2L) + .room(room) + .certification(certification) + .build(); + } + + public static ReportRequest reportRequest() { + return new ReportRequest(reportedId, roomId, certificationId, "description"); + } + + public static ReportRequest reportRequest(Long reportedId, Long roomId, Long certificationId) { + return new ReportRequest(reportedId, roomId, certificationId, "description"); + } +}