Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 리뷰 등록시 요청 DTO Validation 변경 및 로직 추가 #233

Merged
merged 7 commits into from
Feb 16, 2024
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("해당 모임은 아직 종료되지 않았습니다.");
}
}
}
6 changes: 5 additions & 1 deletion src/test/java/net/teumteum/integration/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import net.teumteum.core.config.AppConfig;
Expand Down Expand Up @@ -139,6 +138,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 @@ -67,6 +67,7 @@ public class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

Expand All @@ -88,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 @@ -111,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 @@ -131,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 @@ -153,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 @@ -170,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 @@ -195,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 @@ -210,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
Loading