Skip to content

Commit

Permalink
feat: 리뷰 등록시 회원 매너 온도 반영 기능 추가 및 리뷰 등록 리팩토링 (#158)
Browse files Browse the repository at this point in the history
* refactor: 회원 리뷰 등록 API 리팩토링 (#157)

* feat: 회원 리뷰 등록시, 온도 반영 및 리뷰 피드백 반영 (#157)

* feat: User 엔티티 메소드 추가 (#157)

* feat: Review enum 필드 추가 (#157)

* test: 리뷰 등록시 온도 업데이트에 대한 단위 테스트 (#157)

* test: 리뷰 등록 기능 통합 테스트 (#157)

* refactor: 회원 리뷰 등록 메소드 수정 (#157)

* test: 불필요한 테스트 제거 및 CI 오류 수정 (#157)
  • Loading branch information
choidongkuen authored Jan 28, 2024
1 parent feebed6 commit 8d67d1f
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 39 deletions.
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
28 changes: 25 additions & 3 deletions src/test/java/net/teumteum/unit/user/service/UserServiceTest.java
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

0 comments on commit 8d67d1f

Please sign in to comment.