Skip to content

Commit

Permalink
[KAN-108] 리뷰 테스트 코드 (테스트 커버리지 100%) (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkyoungdeok authored May 30, 2024
1 parent cec9434 commit 4164d5d
Show file tree
Hide file tree
Showing 19 changed files with 1,733 additions and 677 deletions.
24 changes: 2 additions & 22 deletions src/main/kotlin/com/restaurant/be/review/domain/entity/Review.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import com.restaurant.be.common.entity.BaseEntity
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 +44,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,31 +70,15 @@ 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 {
return ReviewResponseDto(
id = id ?: 0,
userId = user.id ?: 0,
username = user.nickname,
profileImageUrl = user.profileImageUrl,
restaurantId = restaurantId,
rating = rating,
content = content,
imageUrls = images.map { it.imageUrl },
isLike = doesUserLike,
viewCount = viewCount,
likeCount = likeCount,
createdAt = createdAt,
modifiedAt = modifiedAt
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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
Expand All @@ -26,8 +25,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,8 +41,7 @@ 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ 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)

Expand All @@ -37,8 +35,7 @@ class GetReviewService(

@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 @@ -57,8 +54,7 @@ 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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,37 @@ 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.review,
reviewWithLikes.isLikedByUser
)

Expand All @@ -53,19 +51,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,13 @@
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 +18,50 @@ 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
review,
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 @@ -59,7 +59,7 @@ data class ReviewResponseDto(
val modifiedAt: LocalDateTime
) {
companion object {
fun toDto(review: Review, isLikedByUser: Boolean? = null): ReviewResponseDto {
fun toDto(review: Review, isLikedByUser: Boolean): ReviewResponseDto {
return ReviewResponseDto(
id = review.id ?: 0,
userId = review.user.id ?: 0,
Expand All @@ -69,7 +69,7 @@ data class ReviewResponseDto(
rating = review.rating,
content = review.content,
imageUrls = review.images.map { it.imageUrl },
isLike = isLikedByUser ?: false,
isLike = isLikedByUser,
likeCount = review.likeCount,
viewCount = review.viewCount,
createdAt = review.createdAt,
Expand Down
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
29 changes: 29 additions & 0 deletions src/test/kotlin/com/restaurant/be/common/util/ReviewUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.restaurant.be.common.util

import com.restaurant.be.review.domain.entity.Review
import com.restaurant.be.review.domain.entity.ReviewImage
import com.restaurant.be.user.domain.entity.User

object ReviewUtil {
fun generateReviewEntity(
id: Long = 0,
user: User,
restaurantId: Long = 0,
content: String = "default_content",
rating: Double = 0.0,
likeCount: Long = 0,
viewCount: Long = 0,
images: MutableList<ReviewImage> = mutableListOf()
): Review {
return Review(
id = id,
user = user,
restaurantId = restaurantId,
content = content,
rating = rating,
likeCount = likeCount,
viewCount = viewCount,
images = images
)
}
}
Loading

0 comments on commit 4164d5d

Please sign in to comment.