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: 리뷰 등록시 회원 매너 온도 반영 기능 추가 및 리뷰 등록 리팩토링 #158

Merged
merged 8 commits into from
Jan 28, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public void registerReview(
@RequestParam Long meetingId,
@Valid @RequestBody ReviewRegisterRequest request
) {
userService.registerReview(meetingId, request);
userService.registerReview(meetingId, getCurrentUserId(), request);
}

@GetMapping("/reviews")
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/net/teumteum/user/domain/Review.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package net.teumteum.user.domain;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum Review {
별로에요,
좋아요,
최고에요
별로에요(-1),
좋아요(1),
최고에요(2);

private final int score;
}
10 changes: 6 additions & 4 deletions src/main/java/net/teumteum/user/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@ public void addFriend(User user) {
friends.add(user.id);
}

public void addReview(Review review) {
List<Review> newReviews = new ArrayList<>(reviews);
newReviews.add(review);
reviews = newReviews;
public void registerReview(Review review) {
reviews.add(review);
}

public void updateMannerTemperature(Review review) {
mannerTemperature += review.getScore();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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

Expand Down
22 changes: 17 additions & 5 deletions src/main/java/net/teumteum/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,15 @@ public void logout(Long userId) {


@Transactional
public void registerReview(Long meetingId, ReviewRegisterRequest request) {
public void registerReview(Long meetingId, Long currentUserId, ReviewRegisterRequest request) {
checkMeetingExistence(meetingId);
checkUserNotRegisterSelfReview(request, currentUserId);

request.reviews()
.forEach(userReview -> {
User user = getUser(userReview.id());
user.addReview(userReview.review());
user.registerReview(userReview.review());
user.updateMannerTemperature(userReview.review());
});
}

Expand Down Expand Up @@ -148,8 +150,18 @@ private void checkUserExistence(Authenticated authenticated, String oauthId) {
}

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

private void checkUserNotRegisterSelfReview(ReviewRegisterRequest request, Long currentUserId) {
Assert.isTrue(request.reviews().stream().noneMatch(review -> review.id().equals(currentUserId)),
() -> {
throw new IllegalArgumentException("나의 리뷰에 대한 리뷰를 작성할 수 없습니다.");
}
);
}
}
11 changes: 11 additions & 0 deletions src/test/java/net/teumteum/integration/Api.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.teumteum.meeting.config.PageableHandlerMethodArgumentResolver;
import net.teumteum.meeting.domain.Topic;
import net.teumteum.teum_teum.domain.request.UserLocationRequest;
import net.teumteum.user.domain.request.ReviewRegisterRequest;
import net.teumteum.user.domain.request.UserRegisterRequest;
import net.teumteum.user.domain.request.UserUpdateRequest;
import net.teumteum.user.domain.request.UserWithdrawRequest;
Expand Down Expand Up @@ -188,4 +189,14 @@ ResponseSpec getUserReviews(String accessToken) {
.header(HttpHeaders.AUTHORIZATION, accessToken)
.exchange();
}

ResponseSpec registerUserReview(String accessToken, Long meetingId, ReviewRegisterRequest request) {
String uri = "/users/reviews?meetingId=" + meetingId;
return webTestClient
.post()
.uri(uri)
.header(HttpHeaders.AUTHORIZATION, accessToken)
.bodyValue(request)
.exchange();
}
}
8 changes: 8 additions & 0 deletions src/test/java/net/teumteum/integration/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


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 @@ -40,6 +41,13 @@ public User saveAndGetUser(Long id) {
return userRepository.saveAndFlush(user);
}

public List<User> saveAndGetUsers(int size) {
return Stream.generate(UserFixture::getNullIdUser)
.limit(size)
.map(userRepository::saveAndFlush)
.toList();
}

List<User> getAllUser() {
return userRepository.findAll();
}
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/net/teumteum/integration/RequestFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import static net.teumteum.user.domain.Review.최고에요;

import java.util.List;
import java.util.Random;
import java.util.UUID;
import net.teumteum.core.security.Authenticated;
import net.teumteum.user.domain.Review;
import net.teumteum.user.domain.User;
import net.teumteum.user.domain.request.ReviewRegisterRequest;
import net.teumteum.user.domain.request.ReviewRegisterRequest.UserReviewRegisterRequest;
Expand Down Expand Up @@ -62,11 +64,24 @@ public static ReviewRegisterRequest reviewRegisterRequest() {
return new ReviewRegisterRequest(userReviewRegisterRequests());
}

public static ReviewRegisterRequest reviewRegisterRequest(List<User> users) {
return new ReviewRegisterRequest(userReviewRegisterRequests(users));
}

private static List<UserReviewRegisterRequest> userReviewRegisterRequests() {
return List.of(new UserReviewRegisterRequest(1L, 별로에요), new UserReviewRegisterRequest(2L, 최고에요),
new UserReviewRegisterRequest(3L, 좋아요));
}

private static List<UserReviewRegisterRequest> userReviewRegisterRequests(List<User> users) {
Review[] reviews = Review.values();
int length = reviews.length;

return users.stream()
.map(user -> new UserReviewRegisterRequest(user.getId(), reviews[new Random().nextInt(length)]))
.toList();
}

private static Job job(User user) {
return new Job(user.getJob().getName(),
user.getJob().getJobClass(),
Expand Down
78 changes: 57 additions & 21 deletions src/test/java/net/teumteum/integration/UserIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

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 @@ -253,25 +256,6 @@ void Withdraw_user_info_api() {
assertThatCode(() -> api.withdrawUser(VALID_TOKEN, request))
.doesNotThrowAnyException();
}

@Test
@DisplayName("해당 회원이 존재하지 않으면, 500 에러를 반환한다.")
void Return_500_error_if_user_not_exist() {
// given
repository.clearUserRepository();

var request = RequestFixture.userWithdrawRequest(List.of("쓰지 않는 앱이에요", "오류가 생겨서 쓸 수 없어요"));

// when
var result = api.withdrawUser(VALID_TOKEN, request);

// then
Assertions.assertThat(result.expectStatus().is5xxServerError()
.expectBody(ErrorResponse.class)
.returnResult()
.getResponseBody())
.usingRecursiveComparison().isNull();
}
}

@Nested
Expand Down Expand Up @@ -341,6 +325,9 @@ class Logout_user_api {
void Logout_user() {
// given
var existUser = repository.saveAndGetUser();

securityContextSetting.set(existUser.getId());

redisRepository.saveRedisDataWithExpiration(String.valueOf(existUser.getId()), VALID_TOKEN, DURATION);

// when & then
Expand All @@ -351,11 +338,11 @@ void Logout_user() {

@Nested
@DisplayName("회원 리뷰 조회 API는")
class Get_user_review_api {
class Get_user_reviews_api {

@Test
@DisplayName("userId 유저의 리뷰 정보를 가져온다.")
void Get_user_review() {
void Get_user_reviews() {
// given
var existUser = repository.saveAndGetUser();

Expand All @@ -373,4 +360,53 @@ void Get_user_review() {
.isNotNull();
}
}

@Nested
@DisplayName("회원 리뷰 등록 API는")
class Register_user_review_api {

User existUser;

List<User> users;

ReviewRegisterRequest request;

Meeting meeting;

@BeforeEach
void setUp() {
existUser = repository.saveAndGetUser();
users = repository.saveAndGetUsers(3);
request = RequestFixture.reviewRegisterRequest(users);
meeting = repository.saveAndGetOpenMeetings(1).get(0);
}

@Test
@DisplayName("회원 리뷰 등록 요청이 들어오면 리뷰를 등록하고, 200 OK 을 반환한다.")
void Return_200_OK_with_success_register_user_review() {
// given
securityContextSetting.set(existUser.getId());

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

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

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

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

// then
Assertions.assertThat(expected.expectStatus().isBadRequest()
.expectBody(ErrorResponse.class)
.returnResult().getResponseBody());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doThrow;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
Expand Down Expand Up @@ -163,6 +165,30 @@ void Register_user_review_with_200_ok() throws Exception {
.andDo(print())
.andExpect(status().isOk());
}

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

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

doThrow(new IllegalArgumentException(errorMessage))
.when(userService)
.registerReview(anyLong(), anyLong(), any(ReviewRegisterRequest.class));

// when & then
mockMvc.perform(post("/users/reviews")
.param("meetingId", String.valueOf(1L))
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reviewRegisterRequest))
.with(csrf())
.header(AUTHORIZATION, VALID_ACCESS_TOKEN))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(result -> assertEquals(errorMessage, result.getResolvedException().getMessage()));
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ void Register_user_review_with_200_ok() {

Long meetingId = 1L;

Long userId = 1L;
Long userId = 10L;

Long currentUserId = 20L;

given(meetingConnector.existById(anyLong()))
.willReturn(true);
Expand All @@ -157,25 +159,45 @@ void Register_user_review_with_200_ok() {
.willReturn(Optional.of(UserFixture.getUserWithId(userId++)));

// when
userService.registerReview(meetingId, reviewRegisterRequest);
userService.registerReview(meetingId, currentUserId, reviewRegisterRequest);

// then
verify(meetingConnector, times(1)).existById(anyLong());
verify(userRepository, times(3)).findById(anyLong());
}

@Test
@DisplayName("회원 id 가 리뷰 정보 요청에 포함되면, 400 Bad Request 와 함께 리뷰 등록을 실패한다.")
void Return_400_bad_request_if_current_user_id_in_request() {
// given
ReviewRegisterRequest reviewRegisterRequest = RequestFixture.reviewRegisterRequest();

Long meetingId = 1L;

Long currentUserId = reviewRegisterRequest.reviews().get(0).id();

given(meetingConnector.existById(anyLong()))
.willReturn(true);

// when & then
assertThatThrownBy(() -> userService.registerReview(meetingId, currentUserId, reviewRegisterRequest))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("나의 리뷰에 대한 리뷰를 작성할 수 없습니다.");
}

@Test
@DisplayName("meeting id 에 해당하는 meeting 이 존재하지 않는 경우, 400 Bad Request 와 함께 리뷰 등록을 실패한다.")
void Return_400_bad_request_if_meeting_is_not_exist() {
// given
ReviewRegisterRequest reviewRegisterRequest = RequestFixture.reviewRegisterRequest();

Long meetingId = 1L;
Long currentUserId = 1L;

given(meetingConnector.existById(anyLong()))
.willReturn(false);
// when & then
assertThatThrownBy(() -> userService.registerReview(meetingId, reviewRegisterRequest))
assertThatThrownBy(() -> userService.registerReview(meetingId, currentUserId, reviewRegisterRequest))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("meetingId에 해당하는 meeting을 찾을 수 없습니다. \"" + meetingId + "\"");
}
Expand Down
Loading
Loading