Skip to content

Commit

Permalink
[KAN-108] 리뷰 테스트 코드 (테스트 커버리지 100%)
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkyoungdeok committed May 30, 2024
1 parent cec9434 commit 3edb7ed
Show file tree
Hide file tree
Showing 19 changed files with 1,739 additions and 703 deletions.
11 changes: 5 additions & 6 deletions src/main/kotlin/com/restaurant/be/review/domain/entity/Review.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import com.restaurant.be.common.exception.InvalidLikeCountException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.user.domain.entity.QUser.user
import com.restaurant.be.user.domain.entity.User
import kotlinx.serialization.json.JsonNull.content
import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.Entity
Expand Down Expand Up @@ -47,7 +45,6 @@ class Review(
@Column(name = "view_count", nullable = false)
var viewCount: Long = 0,

// 부모 (Review Entity)가 주인이되어 Image참조 가능. 반대는 불가능
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
@JoinColumn(name = "review_id")
var images: MutableList<ReviewImage> = mutableListOf()
Expand All @@ -74,17 +71,19 @@ class Review(
fun incrementViewCount() {
this.viewCount++
}

fun incrementLikeCount() {
this.likeCount++
}

fun decrementLikeCount() {
if (this.likeCount == 0L) {
throw InvalidLikeCountException()
}
this.likeCount--
}

fun toResponseDTO(doesUserLike: Boolean): ReviewResponseDto {
fun toDto(isLikedByUser: Boolean): ReviewResponseDto {
return ReviewResponseDto(
id = id ?: 0,
userId = user.id ?: 0,
Expand All @@ -94,9 +93,9 @@ class Review(
rating = rating,
content = content,
imageUrls = images.map { it.imageUrl },
isLike = doesUserLike,
viewCount = viewCount,
isLike = isLikedByUser,
likeCount = likeCount,
viewCount = viewCount,
createdAt = createdAt,
modifiedAt = modifiedAt
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundRestaurantException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.restaurant.repository.RestaurantRepository
import com.restaurant.be.review.domain.entity.ReviewImage
import com.restaurant.be.review.presentation.dto.CreateReviewResponse
import com.restaurant.be.review.presentation.dto.common.ReviewRequestDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
Expand All @@ -26,8 +24,7 @@ class CreateReviewService(
reviewRequest: ReviewRequestDto,
email: String
): CreateReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val review = reviewRequest.toEntity(user, restaurantId)

Expand All @@ -43,13 +40,9 @@ class CreateReviewService(

applyReviewCountAndAvgRating(restaurantId, reviewRequest.rating)

val reviewWithLikes = reviewRepository.findReview(user, review.id ?: 0)
?: throw NotFoundReviewException()
val reviewWithLikes = reviewRepository.findReview(user, review.id ?: 0)!!

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
)
val responseDto = review.toDto(reviewWithLikes.isLikedByUser)

return CreateReviewResponse(responseDto)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.review.presentation.dto.GetMyReviewsResponse
import com.restaurant.be.review.presentation.dto.GetReviewResponse
import com.restaurant.be.review.presentation.dto.GetReviewsResponse
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.data.domain.Pageable
Expand All @@ -16,29 +15,23 @@ import org.springframework.transaction.annotation.Transactional
class GetReviewService(
private val userRepository: UserRepository,
private val reviewRepository: ReviewRepository

) {
@Transactional(readOnly = true)
fun getReviews(pageable: Pageable, restaurantId: Long, email: String): GetReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findReviews(user, restaurantId, pageable)

return GetReviewsResponse(
reviewsWithLikes.map {
ReviewResponseDto.toDto(
it.review,
it.isLikedByUser
)
it.review.toDto(it.isLikedByUser)
}
)
}

@Transactional
fun getReview(reviewId: Long, email: String): GetReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()
Expand All @@ -47,8 +40,7 @@ class GetReviewService(
reviewWithLikes.review.incrementViewCount()
}

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
val responseDto = reviewWithLikes.review.toDto(
reviewWithLikes.isLikedByUser
)

Expand All @@ -57,17 +49,13 @@ class GetReviewService(

@Transactional(readOnly = true)
fun getMyReviews(pageable: Pageable, email: String): GetMyReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findMyReviews(user, pageable)

return GetMyReviewsResponse(
reviewsWithLikes.map {
ReviewResponseDto.toDto(
it.review,
it.isLikedByUser
)
it.review.toDto(it.isLikedByUser)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,35 @@ import com.restaurant.be.common.exception.DuplicateLikeException
import com.restaurant.be.common.exception.NotFoundLikeException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.NotFoundUserIdException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.LikeReviewRequest
import com.restaurant.be.review.presentation.dto.LikeReviewResponse
import com.restaurant.be.review.presentation.dto.ReviewWithLikesDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewLikesRepository
import com.restaurant.be.review.repository.ReviewLikeRepository
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.domain.entity.User
import com.restaurant.be.user.repository.UserRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class LikeReviewService(
val userRepository: UserRepository,
val reviewLikesRepository: ReviewLikesRepository,
val reviewLikeRepository: ReviewLikeRepository,
val reviewRepository: ReviewRepository
) {
@Transactional
fun likeReview(reviewId: Long, request: LikeReviewRequest, email: String): LikeReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val userId = user.id ?: throw NotFoundUserIdException()
val userId = user.id ?: 0L

likeReviewWhetherAlreadyLikeOrNot(request, reviewId, user, userId)

val reviewWithLikes: ReviewWithLikesDto? = reviewRepository.findReview(user, reviewId)
val reviewWithLikes: ReviewWithLikesDto = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes!!.review,
reviewWithLikes.isLikedByUser
)
val responseDto = reviewWithLikes.review.toDto(reviewWithLikes.isLikedByUser)

return LikeReviewResponse(responseDto)
}
Expand All @@ -53,19 +47,19 @@ class LikeReviewService(
if (isAlreadyLike(reviewId, user)) {
throw DuplicateLikeException()
}
reviewLikesRepository.save(request.toEntity(userId, reviewId))
val review = reviewRepository.findById(reviewId)
review.get().incrementLikeCount()
reviewLikeRepository.save(request.toEntity(userId, reviewId))
val review = reviewRepository.findByIdOrNull(reviewId)
review?.incrementLikeCount()
} else {
if (!isAlreadyLike(reviewId, user)) {
throw NotFoundLikeException()
}
reviewLikesRepository.deleteByReviewIdAndUserId(reviewId, userId)
val review = reviewRepository.findById(reviewId)
review.get().decrementLikeCount()
reviewLikeRepository.deleteByReviewIdAndUserId(reviewId, userId)
val review = reviewRepository.findByIdOrNull(reviewId)
review?.decrementLikeCount()
}
}

private fun isAlreadyLike(reviewId: Long, user: User) =
reviewLikesRepository.existsByReviewIdAndUserId(reviewId, user.id)
reviewLikeRepository.existsByReviewIdAndUserId(reviewId, user.id)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundRestaurantException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.UnAuthorizedUpdateException
import com.restaurant.be.restaurant.repository.RestaurantRepository
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.UpdateReviewResponse
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewLikeRepository
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
Expand All @@ -19,37 +17,48 @@ import kotlin.jvm.optionals.getOrNull
class UpdateReviewService(
private val reviewRepository: ReviewRepository,
private val userRepository: UserRepository,
private val restaurantRepository: RestaurantRepository
private val restaurantRepository: RestaurantRepository,
private val reviewLikeRepository: ReviewLikeRepository
) {
@Transactional
fun updateReview(restaurantId: Long, reviewId: Long, reviewRequest: UpdateReviewRequest, email: String): UpdateReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()
fun updateReview(
restaurantId: Long,
reviewId: Long,
reviewRequest: UpdateReviewRequest,
email: String
): UpdateReviewResponse {
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

val review = reviewRepository.findById(reviewId)
.getOrNull()
?: throw NotFoundReviewException()
.orElseThrow { NotFoundReviewException() }

if (user.id != review.user.id) throw UnAuthorizedUpdateException()

applyReviewCountAndAvgRating(review.restaurantId, review.rating, reviewRequest.review.rating)
applyReviewCountAndAvgRating(
review.restaurantId,
review.rating,
reviewRequest.review.rating
)

review.updateReview(reviewRequest)

val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()
reviewRepository.save(review)

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
)
val responseDto =
review.toDto(reviewLikeRepository.existsByReviewIdAndUserId(reviewId, user.id))

return UpdateReviewResponse(responseDto)
}

private fun applyReviewCountAndAvgRating(restaurantId: Long, rating: Double, updateRating: Double) {
private fun applyReviewCountAndAvgRating(
restaurantId: Long,
rating: Double,
updateRating: Double
) {
val restaurant = restaurantRepository.findById(restaurantId).getOrNull()
?: throw NotFoundRestaurantException()
restaurant.updateReview(rating, updateRating)
if (restaurant != null) {
restaurant.updateReview(rating, updateRating)
restaurantRepository.save(restaurant)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
@file:Suppress("ktlint", "MatchingDeclarationName")

package com.restaurant.be.review.presentation.dto

import com.restaurant.be.review.presentation.dto.common.ReviewRequestDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import io.swagger.annotations.ApiModelProperty

data class CreateReviewRequest(
@ApiModelProperty(value = "리뷰 정보", required = true)
val review: ReviewRequestDto
)

data class CreateReviewResponse(
@ApiModelProperty(value = "리뷰 정보", required = true)
val review: ReviewResponseDto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,4 @@ data class ReviewResponseDto(
val createdAt: LocalDateTime,
@Schema(description = "리뷰 수정 시간")
val modifiedAt: LocalDateTime
) {
companion object {
fun toDto(review: Review, isLikedByUser: Boolean? = null): ReviewResponseDto {
return ReviewResponseDto(
id = review.id ?: 0,
userId = review.user.id ?: 0,
username = review.user.nickname,
profileImageUrl = review.user.profileImageUrl,
restaurantId = review.restaurantId,
rating = review.rating,
content = review.content,
imageUrls = review.images.map { it.imageUrl },
isLike = isLikedByUser ?: false,
likeCount = review.likeCount,
viewCount = review.viewCount,
createdAt = review.createdAt,
modifiedAt = review.modifiedAt
)
}
}
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface ReviewLikesRepository : JpaRepository<ReviewLike, Long> {
interface ReviewLikeRepository : JpaRepository<ReviewLike, Long> {
fun existsByReviewIdAndUserId(reviewId: Long?, userId: Long?): Boolean
fun deleteByReviewIdAndUserId(reviewId: Long?, userId: Long?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ class ReviewRepositoryCustomImpl(
.and(reviewLike.userId.eq(user.id))
)
.where(review.id.eq(reviewId))
.fetchJoin()
.fetchOne()
}

override fun findReviews(user: User, restaurantId: Long, pageable: Pageable): Page<ReviewWithLikesDto> {
val orderSpecifier = setOrderSpecifier(pageable)
val orderSpecifier = if (!pageable.sort.isEmpty) {
setOrderSpecifier(pageable)
} else {
val reviewPath = PathBuilderFactory().create(Review::class.java)
listOf(reviewPath.getNumber("id", Long::class.java).desc())
}

val reviewsWithLikes = queryFactory
.select(
Expand Down Expand Up @@ -72,7 +76,7 @@ class ReviewRepositoryCustomImpl(
}

override fun findMyReviews(user: User, pageable: Pageable): Page<ReviewWithLikesDto> {
val orderSpecifier = if (pageable.sort.isSorted) {
val orderSpecifier = if (!pageable.sort.isEmpty) {
setOrderSpecifier(pageable)
} else {
val reviewPath = PathBuilderFactory().create(Review::class.java)
Expand Down
Loading

0 comments on commit 3edb7ed

Please sign in to comment.