Skip to content

Commit

Permalink
feat: 리뷰 등록시 요청 DTO Validation 변경 및 로직 추가 (#233)
Browse files Browse the repository at this point in the history
* feat: 요청 validation 변경 (#228)

* test: UserControllerTest 리팩토링 (#224)

* feat: 리뷰 등록시 userService 로직 추가 (#224)

* test: 테스트 데이터 관련 메소드 추가 (#224)

* test: 리뷰 등록시 로직 추가에 따른 통합 테스트 추가 (#224)

* test: 리뷰 등록시 로직 추가에 따른 Service 단위 테스트 추가 (#224)

* feat: Cors 허용 메소드 추가 (#224)
  • Loading branch information
choidongkuen committed Feb 16, 2024
1 parent a212a38 commit 824c4a8
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "TRACE"));
config.addExposedHeader("Authorization");
config.addExposedHeader("Authorization-refresh");
config.setAllowCredentials(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

public record ReviewRegisterRequest(
@Valid
@Size(min = 2, max = 5)
@Size(min = 1, max = 5)
List<UserReviewRegisterRequest> reviews
) {

Expand Down
28 changes: 21 additions & 7 deletions src/main/java/net/teumteum/user/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package net.teumteum.user.service;

import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import net.teumteum.core.security.Authenticated;
import net.teumteum.core.security.service.JwtService;
import net.teumteum.core.security.service.RedisService;
import net.teumteum.core.security.service.SecurityService;
import net.teumteum.meeting.domain.Meeting;
import net.teumteum.meeting.domain.MeetingConnector;
import net.teumteum.user.domain.BalanceGameType;
import net.teumteum.user.domain.InterestQuestion;
Expand Down Expand Up @@ -109,7 +111,10 @@ public void logout(Long userId) {

@Transactional
public void registerReview(Long meetingId, Long currentUserId, ReviewRegisterRequest request) {
checkMeetingExistence(meetingId);
var meeting = getMeeting(meetingId);

checkMeetingIsClosed(meeting);
checkUserParticipationInMeeting(meeting, currentUserId);
checkUserNotRegisterSelfReview(request, currentUserId);

request.reviews()
Expand Down Expand Up @@ -157,12 +162,9 @@ private void checkUserExistence(Authenticated authenticated, String oauthId) {
);
}

private void checkMeetingExistence(Long meetingId) {
Assert.isTrue(meetingConnector.existById(meetingId),
() -> {
throw new IllegalArgumentException("meetingId에 해당하는 meeting을 찾을 수 없습니다. \"" + meetingId + "\"");
}
);
private Meeting getMeeting(Long meetingId) {
return meetingConnector.findById(meetingId)
.orElseThrow(() -> new IllegalArgumentException("meetingId에 해당하는 모임을 찾을 수 없습니다. \"" + meetingId + "\""));
}

private void checkUserNotRegisterSelfReview(ReviewRegisterRequest request, Long currentUserId) {
Expand All @@ -172,4 +174,16 @@ private void checkUserNotRegisterSelfReview(ReviewRegisterRequest request, Long
}
);
}

private void checkUserParticipationInMeeting(Meeting meeting, Long userId) {
if (!meeting.getParticipantUserIds().contains(userId)) {
throw new IllegalArgumentException("모임에 참여하지 않은 회원입니다.");
}
}

private void checkMeetingIsClosed(Meeting meeting) {
if (!LocalDateTime.now().isAfter(meeting.getPromiseDateTime())) {
throw new IllegalArgumentException("해당 모임은 아직 종료되지 않았습니다.");
}
}
}
5 changes: 5 additions & 0 deletions src/test/java/net/teumteum/integration/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ List<Meeting> saveAndGetCloseMeetingsByParticipantUserId(int size, Long particip
return meetingRepository.saveAllAndFlush(meetings);
}

Meeting saveAndGetCloseMeetingByParticipantUserIds(List<Long> participantUserIds) {
var meeting = MeetingFixture.getCloseMeetingWithParticipantIds(participantUserIds);
return meetingRepository.save(meeting);
}

List<Meeting> saveAndGetOpenMeetings(int size) {
var meetings = Stream.generate(MeetingFixture::getOpenMeeting)
.limit(size)
Expand Down
94 changes: 72 additions & 22 deletions src/test/java/net/teumteum/integration/UserIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@

import java.util.List;
import net.teumteum.core.error.ErrorResponse;
import net.teumteum.meeting.domain.Meeting;
import net.teumteum.user.domain.User;
import net.teumteum.user.domain.UserFixture;
import net.teumteum.user.domain.request.ReviewRegisterRequest;
import net.teumteum.user.domain.response.FriendsResponse;
import net.teumteum.user.domain.response.UserGetResponse;
import net.teumteum.user.domain.response.UserMeGetResponse;
import net.teumteum.user.domain.response.UserRegisterResponse;
import net.teumteum.user.domain.response.UserReviewsResponse;
import net.teumteum.user.domain.response.UsersGetByIdResponse;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -341,48 +338,101 @@ void Get_user_reviews() {
@DisplayName("회원 리뷰 등록 API는")
class Register_user_review_api {

User existUser;
@Test
@DisplayName("정상적인 요청이 오는 경우, 해당 회원의 리뷰 등록과 함께 200 OK 을 반환한다.")
void Return_200_OK_and_register_review_if_request_is_valid() {
// given
var user = repository.saveAndGetUser();
var participant1 = repository.saveAndGetUser();
var participant2 = repository.saveAndGetUser();

List<User> users;
var closedMeeting = repository.saveAndGetCloseMeetingByParticipantUserIds(
List.of(user.getId(), participant1.getId(), participant2.getId()));

ReviewRegisterRequest request;
var request = RequestFixture.reviewRegisterRequest(List.of(participant1, participant2));

Meeting meeting;
securityContextSetting.set(user.getId());

@BeforeEach
void setUp() {
existUser = repository.saveAndGetUser();
users = repository.saveAndGetUsers(3);
request = RequestFixture.reviewRegisterRequest(users);
meeting = repository.saveAndGetOpenMeetings(1).get(0);
// when
var expected = api.registerUserReview(VALID_TOKEN, closedMeeting.getId(), request);

// then
Assertions.assertThat(expected.expectStatus().isOk());
}

@Test
@DisplayName("회원 리뷰 등록 요청이 들어오면 리뷰를 등록하고, 200 OK 을 반환한다.")
void Return_200_OK_with_success_register_user_review() {
@DisplayName("meeting id 에 해당하는 meeting 이 아직 종료되지 않았다면, 400 Bad Request 와 함께 리뷰 등록을 실패한다.")
void Return_400_bad_request_if_meeting_is_not_closed() {
// given
securityContextSetting.set(existUser.getId());
var user = repository.saveAndGetUser();
var participant = repository.saveAndGetUser();

var openMeeting = repository.saveAndGetOpenMeeting();
var request = RequestFixture.reviewRegisterRequest(List.of(participant));

securityContextSetting.set(user.getId());

// when
var expected = api.registerUserReview(VALID_TOKEN, meeting.getId(), request);
var expected = api.registerUserReview(VALID_TOKEN, openMeeting.getId(), request);

// then
Assertions.assertThat(expected.expectStatus().isOk());
Assertions.assertThat(expected.expectStatus().isBadRequest()
.expectBody(ErrorResponse.class)
.returnResult().getResponseBody())
.extracting(ErrorResponse::getMessage)
.isEqualTo("해당 모임은 아직 종료되지 않았습니다.");
}

@Test
@DisplayName("현재 로그인한 회원의 id 가 리뷰 등록 요청에 포함된다면, 회원 리뷰 등록을 실패하고 400 bad request 을 반환한다.")
void Return_400_bad_request_if_current_user_id_in_request() {
// given
securityContextSetting.set(users.get(0).getId());
var user = repository.saveAndGetUser();
var participant = repository.saveAndGetUser();

var closedMeeting = repository.saveAndGetCloseMeetingByParticipantUserIds(
List.of(user.getId(), participant.getId()));

var request = RequestFixture.reviewRegisterRequest(List.of(user, participant));

securityContextSetting.set(user.getId());

// when
var expected = api.registerUserReview(VALID_TOKEN, closedMeeting.getId(), request);

// then
Assertions.assertThat(expected.expectStatus().isBadRequest()
.expectBody(ErrorResponse.class)
.returnResult().getResponseBody())
.extracting(ErrorResponse::getMessage)
.isEqualTo("나의 리뷰에 대한 리뷰를 작성할 수 없습니다.");
}

@Test
@DisplayName("현재 로그인한 회원의 id 가 모임 참여자에 포함되지 않는다면, 회원 리뷰 등록을 실패하고 400 bad request 을 반환한다.")
void Return_400_bad_request_if_meeting_not_contain_current_user_id_() {
// given
var user = repository.saveAndGetUser();
var participant1 = repository.saveAndGetUser();
var participant2 = repository.saveAndGetUser();

var closedMeeting = repository.saveAndGetCloseMeetingByParticipantUserIds(
List.of(participant1.getId(), participant2.getId()));

var request = RequestFixture.reviewRegisterRequest(List.of(participant1, participant2));

securityContextSetting.set(user.getId());

// when
var expected = api.registerUserReview(VALID_TOKEN, meeting.getId(), request);
var expected = api.registerUserReview(VALID_TOKEN, closedMeeting.getId(), request);

// then
Assertions.assertThat(expected.expectStatus().isBadRequest()
.expectBody(ErrorResponse.class)
.returnResult().getResponseBody());
.expectBody(ErrorResponse.class)
.returnResult().getResponseBody())
.extracting(ErrorResponse::getMessage)
.isEqualTo("모임에 참여하지 않은 회원입니다.");
}
}
}

31 changes: 31 additions & 0 deletions src/test/java/net/teumteum/meeting/domain/MeetingFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,44 @@ public static Meeting getOpenMeeting() {
);
}

public static Meeting getOpenMeetingWithId(Long meetingId) {
return newMeetingByBuilder(MeetingBuilder.builder()
.id(meetingId)
.promiseDateTime(LocalDateTime.of(4000, 1, 1, 0, 0))
.build());

}

public static Meeting getCloseMeeting() {
return newMeetingByBuilder(MeetingBuilder.builder()
.promiseDateTime(LocalDateTime.of(2000, 1, 1, 0, 0))
.build()
);
}

public static Meeting getCloseMeetingWithId(Long meetingId) {
return newMeetingByBuilder(MeetingBuilder.builder()
.id(meetingId)
.promiseDateTime(LocalDateTime.of(2000, 1, 1, 0, 0))
.build());
}

public static Meeting getCloseMeetingWithIdAndParticipantIds(Long meetingId, List<Long> participantIds) {
return newMeetingByBuilder(MeetingBuilder.builder()
.id(meetingId)
.participantUserIds(new HashSet<>(participantIds))
.promiseDateTime(LocalDateTime.of(2000, 1, 1, 0, 0))
.build()
);
}

public static Meeting getCloseMeetingWithParticipantIds(List<Long> participantIds) {
return newMeetingByBuilder(MeetingBuilder.builder()
.participantUserIds(new HashSet<>(participantIds))
.promiseDateTime(LocalDateTime.of(2000, 1, 1, 0, 0))
.build());
}

public static Meeting getOpenFullMeeting() {
return newMeetingByBuilder(MeetingBuilder.builder()
.promiseDateTime(LocalDateTime.of(4000, 1, 1, 0, 0))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ class Register_user_card_api_unit {
@DisplayName("유효한 사용자의 등록 요청값이 주어지면, 201 Created 상태값을 반환한다.")
void Register_user_card_with_201_created() throws Exception {
// given
UserRegisterRequest request = RequestFixture.userRegisterRequest(user);
var request = RequestFixture.userRegisterRequest(user);

UserRegisterResponse response = new UserRegisterResponse(1L, VALID_ACCESS_TOKEN, VALID_REFRESH_TOKEN);
var response = new UserRegisterResponse(1L, VALID_ACCESS_TOKEN, VALID_REFRESH_TOKEN);

given(userService.register(any(UserRegisterRequest.class))).willReturn(response);

Expand All @@ -112,7 +112,7 @@ void Register_user_card_with_201_created() throws Exception {
@DisplayName("이미 카드 등록한 사용자의 등록 요청값이 주어지면, 400 Bad Request을 반환한다.")
void Return_400_bad_request_if_user_already_exist() throws Exception {
// given
UserRegisterRequest request = RequestFixture.userRegisterRequest(user);
var request = RequestFixture.userRegisterRequest(user);

given(userService.register(any(UserRegisterRequest.class)))
.willThrow(new IllegalArgumentException("일치하는 user 가 이미 존재합니다."));
Expand All @@ -132,7 +132,7 @@ void Return_400_bad_request_if_user_already_exist() throws Exception {
@DisplayName("유효하지 않은 사용자의 등록 요청값이 주어지면, 400 Bad Request 상태값을 반환한다.")
void Register_user_card_with_400_bad_request() throws Exception {
// given
UserRegisterRequest request = RequestFixture.userRegisterRequestWithNoValid(user);
var request = RequestFixture.userRegisterRequestWithNoValid(user);
// when
// then
mockMvc.perform(post("/users")
Expand All @@ -154,7 +154,7 @@ class Withdraw_user_api_unit {
@DisplayName("회원 탈퇴 사유와 회원 탈퇴 요청이 들어오면, 탈퇴를 진행하고 200 OK을 반환한다.")
void Withdraw_user_with_200_ok() throws Exception {
// given
UserWithdrawRequest request
var request
= RequestFixture.userWithdrawRequest(List.of("쓰지 않는 앱이에요", "오류가 생겨서 쓸 수 없어요"));

// when & then
Expand All @@ -171,7 +171,7 @@ void Withdraw_user_with_200_ok() throws Exception {
@DisplayName("회원 탈퇴 하고자 하는 회원이 존재하지 않으면, 400 Bad Request을 반환한다.")
void Return_400_bad_request_if_user_is_not_exist() throws Exception {
// given
UserWithdrawRequest request
var request
= RequestFixture.userWithdrawRequest(List.of("쓰지 않는 앱이에요", "오류가 생겨서 쓸 수 없어요"));

doThrow(new IllegalArgumentException("일치하는 user가 이미 존재합니다.")).when(userService).withdraw(any(
Expand All @@ -196,7 +196,7 @@ class Register_user_review_api_unit {
@DisplayName("회원 id 와 리뷰 정보 요청이 들어오면, 회원 리뷰를 등록하고 200 OK을 반환한다.")
void Register_user_review_with_200_ok() throws Exception {
// given
ReviewRegisterRequest reviewRegisterRequest = RequestFixture.reviewRegisterRequest();
var reviewRegisterRequest = RequestFixture.reviewRegisterRequest();

// when & then
mockMvc.perform(post("/users/reviews")
Expand All @@ -211,11 +211,11 @@ void Register_user_review_with_200_ok() throws Exception {

@Test
@DisplayName("현재 로그인한 회원의 id 가 리뷰 등록 요청에 포함된다면, 회원 리뷰 등록을 실패하고 400 bad request을 반환한다.")
void Register_reviews_with_400_bad_request() throws Exception {
void Return_400_bad_request_if_request_contains_current_user_id() throws Exception {
// given
ReviewRegisterRequest reviewRegisterRequest = RequestFixture.reviewRegisterRequest();
var reviewRegisterRequest = RequestFixture.reviewRegisterRequest();

String errorMessage = "나의 리뷰에 대한 리뷰를 작성할 수 없습니다.";
var errorMessage = "나의 리뷰에 대한 리뷰를 작성할 수 없습니다.";

doThrow(new IllegalArgumentException(errorMessage))
.when(userService)
Expand Down
Loading

0 comments on commit 824c4a8

Please sign in to comment.